`ls`를 반복하면 bash 쉘 스크립트가 생성됩니다.


93

ls디렉토리 이름 목록을 위해 무언가를 수행하고 각각을 반복 하고 무언가를 수행하기위한 템플릿 쉘 스크립트가 있습니까?

나는 할 계획입니다 ls -1d */디렉토리 이름의 목록을 얻을 수 있습니다.

답변:


109

@ shawn-j-goff와 다른 사람들이 제안했듯이 글로브가 할 위치에 ls를 사용하지 않도록 편집했습니다.

for..do..done루프를 사용하십시오 .

for f in *; do
  echo "File -> $f"
done

당신은 대체 할 수 **.txt나 (그 문제에 대해 파일, 디렉토리, 또는 아무것도의) 목록을 반환하는 모든 다른 글로브, 목록을 생성하는 명령, 예를 들어,이, $(cat filelist.txt)또는 실제로 목록으로 대체합니다.

do루프 내에서 달러 기호 접두사가 붙은 루프 변수를 참조하면됩니다 ( $f위의 예에서와 같이). 당신이 할 echo수 있거나 원하는 다른 일을 할 수 있습니다.

예를 들어, 모든 이름을 바꾸려면 .xml현재 디렉토리에있는 파일을 .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

또는 Bash를 사용하는 경우 하위 쉘을 생성하는 대신 Bash 매개 변수 확장을 사용할 수 있습니다.

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done

22
파일 이름에 공백이 있으면 어떻게합니까?
Daniel A. White

4
불행히도 Daniel이 말했듯이 파일 또는 폴더에 이름에 공백이나 줄 바꿈이 포함되어 있으면이 답변의 코드가 손상됩니다. for루프의 일반적인 오용 과의 출력을 파싱하려는 전형적인 함정을 보여줍니다 ls. @ DanielA.White, 당신 말한 것처럼 디렉토리에서 작업하고 있기 때문에 도움이되지 않거나 잠재적으로 오도 된 경우이 답변을 수락 하지 않는 것을 고려할 수 있습니다 . Shawn J. Goff의 답변은 문제에 대한보다 강력하고 효과적인 해결책을 제공해야합니다.
slhck

4
-∞ 당신은 구문 분석하지 않아야 ls출력을 , 당신은 출력 읽을해야 for루프를 , 당신은 사용해야합니다 $()``대신하고 더 많은 따옴표를 사용하여 ™ . @CoverosGene이 잘 의미한다고 확신하지만 이것은 끔찍합니다.
l0b0

1
실제로 사용하려는 경우 더 나은 대안 lsls -1 | while read line; do stuff; done입니다. 최소한 그 공백은 깨지지 않습니다.
Emmanuel Joubaud '

1
mv -v당신이 필요하지 않습니다echo "moved $x -> $t"
DmitrySandalov

68

출력을 사용하여 ls파일 이름을 얻는 것은 나쁜 생각 입니다. 오작동이나 위험한 스크립트로 이어질 수 있습니다. 파일 이름을 제외한 모든 문자를 포함 할 수 있기 때문이다 /null문자 및 ls구분 기호로 이러한 문자 중 하나를 사용하지 않기 때문에 파일 이름에 공백이나 줄 바꿈이있는 경우, 당신은 예상치 못한 결과를 얻을 수 있습니다.

파일을 반복하는 두 가지 좋은 방법이 있습니다. 여기서는 단순히 echo파일 이름으로 무언가를 수행하는 것을 보여주기 위해 사용했습니다 . 그래도 무엇이든 사용할 수 있습니다.

첫 번째는 쉘의 기본 글 로빙 기능을 사용하는 것입니다.

for dir in */; do
  echo "$dir"
done

*/for루프가 읽는 별도의 인수로 확장 됩니다 . 파일 이름에 공백, 줄 바꿈 또는 기타 이상한 문자가 있더라도 for각 완전한 이름을 원자 단위로 볼 수 있습니다. 어떤 식 으로든 목록을 구문 분석하지 않습니다.

당신이 하위 디렉토리로 재귀 적으로 가고 싶은 경우 쉘과 같은 몇 가지 확장 로빙 기능 (하지 않는 한, 이것은하지 않을 것이다 bashs '을 (를) globstar쉘이 이러한 기능이없는 경우. 또는 당신이 확인하고 싶은 경우 스크립트가 작동합니다 다양한 시스템에서 다음 옵션은을 사용하는 것 find입니다.

find . -type d -exec echo '{}' \;

여기서 find명령은 echo파일 이름의 인수를 호출 하여 전달합니다. 찾은 각 파일에 대해이 작업을 한 번 수행합니다. 이전 예제와 마찬가지로 파일 이름 목록을 구문 분석하지 않습니다. 대신 파일 이름이 인수로 완전히 전송됩니다.

-exec논쟁 의 구문은 약간 재미있어 보인다. find이후에 첫 번째 인수 -exec를 사용하여 해당 프로그램을 실행할 프로그램으로 처리하고 이후의 모든 인수는 해당 프로그램에 전달하는 인수로 사용합니다. 확인 -exec해야 할 두 가지 특별한 주장이 있습니다. 첫 번째는 {}; 이 인수는 이전 부분이 find생성 하는 파일 이름으로 대체 됩니다. 두 번째는이며 ;, 이것이 find프로그램에 전달할 인수 목록의 끝임을 알려줍니다. 실행 된 프로그램을위한 것이 아닌 find더 많은 인수를 계속할 수 있기 때문에 이것이 필요합니다 find. 그 이유 \는 껍질도 취급하기 때문입니다;특히-명령의 끝을 나타내므로 쉘이 명령 find자체를 소비하는 대신 인수로 제공하도록 명령을 이스케이프 처리해야 합니다. 쉘을 특별하게 취급하지 않도록하는 또 다른 방법은 따옴표로 묶는 것입니다.이 목적 ';'뿐만 아니라 작동합니다 \;.


2
+1 이것은 파일 목록을 생성하고 명령에 사용해야 할 때 확실히가는 방법입니다. find -exec는 단일 명령 만 실행할 수 있으므로 제한됩니다. 루프를 사용하면 심장의 내용물로 파이프 할 수 있습니다.
MaQleod

+1. 더 많은 사람들이 사용하기를 바랍니다 find. 할 수있는 마술이 있으며, 인식 된 한계조차도 -exec해결할 수 있습니다. 이 -print0옵션은와 함께 사용할 수도 있습니다 xargs.
ghoti

루프 옵션은 숨겨진 디렉토리를 표시하지 않습니다. 찾기 옵션은 심볼릭 링크를 표시하지 않습니다.
jinawee

15

공백이있는 파일의 경우 다음과 같이 변수를 인용해야합니다.

 for i in $(ls); do echo "$i"; done; 

또는 입력 필드 구분 기호 (IFS) 환경 변수를 변경할 수 있습니다.

 IFS=$'\n';for file in $(ls); do echo $i; done

마지막으로 수행중인 작업에 따라 ls가 필요하지 않을 수도 있습니다.

 for i in *; do echo "$i"; done;

첫 번째 예제에서 서브 쉘을 잘 사용
Jeremy L

과제 후와 새로운 캐릭터 전에 IFS에 $가 필요한 이유는 무엇입니까?
Andy Ibanez

3
파일 이름에는 줄 바꾸기도 포함될 수 있습니다. 침입 \n이 충분하지 않습니다. 의 출력 구문 분석을 권장하지 않는 것이 좋습니다 ls.
ghoti


4

CoverosGene의 답변에 추가하기 위해 디렉토리 이름 만 나열하는 방법이 있습니다.

for f in */; do
  echo "Directory -> $f"
done

1

왜 IFS를 줄 바꿈으로 설정 ls하고 배열 의 출력을 캡처하지 않습니까? IFS를 줄 바꿈으로 설정하면 파일 이름에 재미있는 문자가있는 문제가 해결됩니다. ls내장 정렬 기능이 있기 때문에 사용하는 것이 좋습니다.

(테스트에서 IFS를 \n설정하는 데 문제가 있었지만 다른 곳에서 제안한 것처럼 줄 바꿈 백 스페이스로 설정하면 작동합니다).

예 : (원하는 ls검색 패턴이 전달 되었다고 가정 $1) :

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

예를 들어, 생성 날짜 (가장 오래된 것부터 가장 오래된 것까지)로 정렬 된 파일 목록을 캡처하기 위해 OS X에서 특히 유용합니다 . ls명령은 ls -t -U -r입니다.


2
파일 이름에는 줄 바꿈이 포함될 수 있으며 사용자가 자신의 파일 이름을 지정할 수있는 경우가 종종 있습니다. 침입 \n이 충분하지 않습니다. 유일하게 유효한 솔루션은 경로 이름이 확장 된 for 루프 또는를 사용합니다 find.
ghoti

파일 이름 목록을 전송하는 신뢰할 수있는 유일한 방법은 파일 경로에 포함되지 않은 유일한 파일 이름이므로 NUL 문자로 구분하는 것입니다.
glglgl

-3

이것이 내가하는 방법이지만 더 효율적인 방법이있을 수 있습니다.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt

6
파일 대신 파이프 ls | while read i; do echo filename: $i; done
Jeremy L

시원한. 두 명령 사이에 $ EDITOR filelist.txt를 사용할 수도 있다고 말해야합니다. 명령 줄보다 쉬운 편집기에서 할 수있는 일이 많습니다. 그러나이 질문과 관련이 없습니다.
TREE

귀하의 솔루션은 개행 및 기타 멋진 것들을 포함하는 파일 이름의 문제를 전혀 해결하지 못합니다.
glglgl
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.