bash : 공백이있는 파일 이동


10

파일 이름에 공백이있는 단일 파일을 이동하면 다음과 같이 작동합니다.

$ mv "file with spaces.txt" "new_place/file with spaces.txt"

이제 공백이 포함될 수있는 파일 목록이 있으며 이동하려고합니다. 예를 들면 다음과 같습니다.

$ echo "file with spaces.txt" > file_list.txt
$ for file in $(cat file_list.txt); do mv "$file" "new_place/$file"; done;

mv: cannot stat 'file': No such file or directory
mv: cannot stat 'with': No such file or directory
mv: cannot stat 'spaces.txt': No such file or directory

첫 번째 예는 왜 효과가 있지만 두 번째 예는 효과가 없습니까? 어떻게 작동시킬 수 있습니까?

답변:


20

절대로 사용하지 마십시오for foo in $(cat bar) . 이것은 일반적으로 bash 함정 번호 1 로 알려진 고전적인 실수입니다. 대신 다음을 사용해야합니다.

while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt

당신이 실행하면 for루프를, bash는 그것이 무엇을 읽고에 wordsplitting 적용됩니다,이 의미하는 것은 a strange blue cloud아니라 읽을 수 있습니다 a, strange, blue과 cloud:

$ cat files 
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt

다음과 비교하십시오 :

$ while IFS= read -r file; do echo "$file"; done < files 
a strange blue cloud.txt

심지어, 당신이 주장하는 경우 UUoC :

$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt

따라서 while루프는 입력을 읽고를 사용하여 read각 줄을 변수에 할당합니다. IFS=세트 NULL로 입력 필드 분리 * , 상기 -r의 옵션 read(즉,가되도록 백 슬래시 이스케이프 해석 정거장 그것을 \t슬래시 +로 취급 t되지 탭으로). --애프터 mv수단 "치료의 모든 후 - 인수가 아닌 옵션으로"당신이로 시작하는 파일 이름을 처리 할 수 있습니다, -정확하게.


* 이것은 엄격하게 말해서 여기에 필요하지는 않습니다.이 시나리오의 유일한 이점은 read선행 또는 후행 공백을 제거하지 않는다는 것입니다. 일반적으로 임의의 파일 이름을 처리 할 수 ​​있어야 할 때.


1
좋아, 나는 "$ file"부분을 벗어날 방법만을 찾고 있었다. 다른 곳에서 오류를 예상하지 못했습니다.
— MrJingles87

알고 있습니다. 이것은 어느 시점에서 모든 사람 을 얻는다 . 당신이 그것에 대해 모른다면 그것은 매우 직관적이지 않습니다.
— terdon

@ JohnKugelman 예, 좋은 지적입니다. 답변이 수정되었습니다. 감사합니다.
— terdon

IFS=파일 이름의 줄 바꿈에는 도움이되지 않습니다. 여기서 파일 형식은 단순히 줄 바꿈이있는 파일 이름을 허용하지 않습니다. IFS=공백이나 탭으로 끝나는 파일 이름 (또는 미리 개행 $ IFS 이외의 문자)에 필요합니다.
— Stéphane Chazelas

그 배치 함정은 더의 출력을 구문 분석하려고에 관한 ls또는 find더 잘 할 수있는, 더 신뢰할 수있는 방법이있는 경우 명령 대체와 함께. $(cat file)제대로하면 괜찮을 것입니다. for + $ (...)와 마찬가지로 while read 루프를 사용하여 "올바르게"수행하는 것은 어렵습니다. 내 대답을 참조하십시오.
— Stéphane Chazelas

11

그건 $(cat file_list.txt)같은 POSIX 쉘에서 bash리스트 문맥에서 분할 + 글로브 연산자는 ( zsh단지 않습니다 분할 예상대로 부분).

글 로빙을 해제하지 않으면 $IFS(기본적으로 SPC , TAB 및 NL) 문자로 분할되고 글로브를 수행하지 않습니다.

여기서는 개행으로 만 나누고 glob 부분을 원하지 않으므로 다음과 같아야합니다.

IFS='
' # split on newline only
set -o noglob # disable globbing

for file in $(cat file_list.txt); do # split+glob
  mv -- "$file" "new_place/$file"
done

그것은 또한 while read빈 줄을 버리고, 끝나지 않은 끝없는 줄을 보존하고, mvstdin (예를 들어 프롬프트의 경우에 필요 )을 유지하는 이점 ( 루프보다)을 가지고 있습니다.

이 파일의 전체 내용 (같은 껍질을 여러 번 메모리에 저장하는 것을 불구하고 단점을 가지고 bash하고 zsh).

일부 쉘 (로 ksh, zsh그리고 낮은 정도 bash), 당신은 그것을 최적화 할 수 있습니다 $(<file_list.txt)대신 $(cat file_list.txt).

while read루프 와 동등한 작업을 수행하려면 다음이 필요합니다.

while IFS= read <&3 -r file || [ -n "$file" ]; do
  {
    [ -n "$file" ] || mv -- "$file" "new_place/$file"
  } 3<&-
done 3< file_list.txt

또는과 bash:

readarray -t files < file_list.txt &&
for file in "${files[@]}"
  [ -n "$file" ] || mv -- "$file" "new_place/$file"
done

또는과 zsh:

for file in ${(f)"$(<file_list.txt)"}
  mv -- "$file" "new_place/$file"
done

또는 GNU mv와 함께 zsh:

mv -t -- new_place ${(f)"$(<file_list.txt)"}

또는 GNU mv및 GNU xargs및 ksh / zsh / bash를 사용하십시오.

xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place

bash / POSIX 쉘에서 변수를 인용하는 것을 잊어 버린 보안 함의 에서 확장을 인용하지 않은 것이 의미하는 것에 대한 자세한 내용


그러나 $(cat file_list.txt)반복하기 전에 전체 파일을 메모리로 while read ...읽고 한 번에 한 줄만 읽는다 는 것을 인정해야한다고 생각합니다 . 큰 파일의 경우 문제가 될 수 있습니다.
— chepner

@chepner. 좋은 지적. 추가되었습니다.
— Stéphane Chazelas

1

스크립트를 작성하는 대신 find ..를 사용할 수 있습니다.

 find -type f -iname \*.txt -print0 | xargs -IF -0 mv F /folder/to/destination/

파일이 파일에있는 경우 다음을 수행 할 수 있습니다.

cat file_with_spaces.txt | xargs -IF -0 mv F /folder/to/destination

두 번째 확실하지 않은 ..

행운을 빕니다


3
를 find사용하는 find경우 모든 용도로 사용할 수 있습니다 . 왜 가져와 xargs? find -type f -iname '*.txt' -exec mv {} /folder/to/destination/.
— terdon

xargs는 단순히 find의 결과를 조작합니다. -I는 모든 결과를 변수 F에 할당하고 0은 print0은 공백이있는 파일이 이스케이프되지 않도록합니다. -0은 xargs가 결과를 피하는 것을 방지합니다. -exec가 공백과 어떻게 동작하는지 잘 모르겠습니다.
— Noel Alex Makumuli

2
-exec임의의 파일 이름 (공백, 줄 바꿈 또는 기타 이상한 점이 포함 된 파일 이름 포함)과 완벽하게 작동합니다. 나는 그것이 어떻게 xargs작동 하는지 알고 있습니다. :) 나는 find이미 당신을 위해 그것을 할 수 있을 때 별도의 명령을 호출하는 요점을 보지 못합니다 . 아무런 문제가 없으며 비효율적입니다.
— terdon

@terdonyou 맞습니다 .. -exec {} / path / destination이 원활하게 작동합니다. 더 많은 일을 할 수 있기 때문에 xargs를 선호합니다.
— Noel Alex Makumuli

xargs또한 clobbering stdin ( mv프롬프트 가 필요함) 의 단점이 있습니다. @terdon의 솔루션에도 문제가 있습니다. xargs프로세스 대체 기능 이있는 GNU 및 쉘을 사용하면 다음 과 같이 완화 할 수 있습니다.xargs -0IF -a <(find...) mv F /dest
— Stéphane Chazelas

-1
FIRST INSTALL 

apt-get install chromium-browser

apt-get install omxplayer

apt-get install terminator

apt-get install nano (if not already installed)

apt-get install zenity (if not already installed)

THEN CREATE A BASH SCRIPT OF ALL OF THESE PERSONALLY WRITTEN SCRIPTS. 

MAKE SURE THAT EVERY SHELL SCRIPT IS A .sh FILE ENDING EACH ONE OF THESE IS SCRIPTED TO OPEN UP A DIRECTORY FOR YOU TO FIND AND PICK WHAT YOU WANT. 

IT RUNS A BACKGROUND TERMINAL & ALLOWS YOU TO CONTROL THE SONG OR MOVIE.  

MOVIE KEYS ARE AS FOLLOWS; p or space bar for pause, q for quit, - & + for sound up and down, left arrow and right arrow for skipping forward and back. 

MUSIC PLAYER REALLY ONLY HAS A SKIP SONG AND THAT IS CTRL+C AND IF THAT IS THE LAST SONG OR ONLY SONG THEN IT SHUTS DOWN AND THE TERMINAL GOES AWAY.


****INSTRUCTIONS TO MAKE THE SCRIPTS****
- OPEN UP A TERMINAL 

- CD TO THE DIRECTORY YOU WANT THE SCRIPTS TO BE
cd /home/pi/Desktop/

- OPEN UP THE NANO EDITOR WITH THE TITLE OF SHELL YOU WANT
sudo nano Movie_Player.sh

- INSIDE NANO, TYPE OR COPY/PAST (REMEMBER THAT IN TERMINAL YOU NEED TO CTRL+SHIFT+V) THE SCRITP

- SAVE THE DATA WITH CTRL+O
ctrl+o

- HIT ENTER TO SAVE AS THAT FILE NAME OR DELETE THE FILE NAME THEN TYPE NEW ONE JUST MAKE SURE IT ENDS IN .sh THEN HIT ENTER

- NEXT YOU NEED TO SUDO CHMOD IT WITH +X TO MAKE IT CLICKABLE AS A BASH
sudo chmod +x Movie_Player.sh

- FINALLY RUN IT TO TEST EITHER BY DOUBLE CLICKING IT AND CHOOSING "EXECUTE IN TERMINAL" OR BY ./ IT IN TERMINAL
./Movie_Player.sh

YOU ARE NOW GOOD TO PICK A MOVIE OR SELECT A SONG OR ALBUM AND ENJOY!  




**** ALL OF THESE SCRIPTS ACCOUNT FOR SPACES IN THE FILENAME 
SO YOU CAN ACCESS "TOM PETTY" OR "SIXTEEN STONE" WITHOUT NEEDING THE " _ " BETWEEN THE WORDS.




-------WATCH A MOVIE SCRIPT ------- (

#! / bin / bash

파일 =zenity --title "Pick a Movie" --file-selection

"$ {FILE [0]}"의 파일

omxplayer "$ {FILE [0]}"완료

--------LISTEN TO A SONG -------

! / bin / bash

파일 =zenity --title "Pick a Song" --file-selection

"$ {FILE [0]}"의 파일

"$ {FILE [0]}"을 (를) 수행함

--------LISTEN TO AN ALBUM ---------- SONGS IN ORDER

! / bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

"$ {DIR [0]}"의 DIR에 대한

cd "$ {DIR [0]}"&& find를 수행하십시오. -type f -name ' .ogg'-o -name ' .mp3'| sort --version-sort | DIR을 읽는 동안; "$ DIR"을 다 했습니까?

--------LISTEN TO AN ALBUM WITH SONGS IN RANDOM ORDER --------

! / bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

"$ {DIR [0]}"의 DIR에 대한

cd "$ {DIR [0]}"&& find를 수행하십시오. -type f -name ' .ogg'-o -name ' .mp3'| DIR을 읽는 동안; "$ DIR"을 다 했습니까?


마크 다운에 대해 읽고 답변에 약간의 메이크업을 적용하십시오. 그리고 이것이 왜 질문에 대답하는지 설명하십시오. 그리고 소리 지르지 마라 !!!

이 답변이이 질문에 속하지 않는다고 생각합니다. 그러나 그렇다면 간단한 문제를 해결하기 위해 많은 소프트웨어를 설치해야합니다.
— MrJingles87
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.