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, bluecloud:

$ 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.