파일의 각 줄에 대해 명령을 어떻게 실행합니까?


161

예를 들어, 지금은 유닉스 경로를 파일에 쓴 두 개의 파일을 변경하기 위해 다음을 사용하고 있습니다.

cat file.txt | while read in; do chmod 755 "$in"; done

더 우아하고 안전한 방법이 있습니까?

답변:


127

파일을 한 줄씩 읽고 명령을 실행하십시오.

답변이 하나뿐이 아니기 때문에 ...

  1. shell 명령 줄 확장
  2. xargs 전용 도구
  3. while read 약간의 언급과 함께
  4. while read -u대화식 처리를 fd위해 전용 사용 (샘플)

영업 요청에 대해서 : 실행 chmod파일에 나열된 모든 대상에하는 것은 , xargs지정된 도구입니다. 그러나 다른 응용 프로그램, 소량의 파일 등의 경우 ...

  1. 전체 파일을 명령 줄 인수로 읽습니다.

    파일이 너무 크지 않고 모든 파일 이름 이 공백이나 따옴표와 같은 다른 특수 문자없이 잘 지정되어 있으면 shell명령 줄 확장을 사용할 수 있습니다 . 간단히:

    chmod 755 $(<file.txt)

    적은 양의 파일 (줄)의 경우이 명령이 더 밝습니다.

  2. xargs 올바른 도구입니다

    더 많은 양의 파일 또는 입력 파일의 거의 모든 행 수

    많은 바이너리 유틸리티의 도구, 등 chown, chmod, rm, cp -t...

    xargs chmod 755 <file.txt

    당신은 특수 문자 및 / 또는 라인의 많은 경우 file.txt.

    xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)

    항목별로 명령을 정확히 1 회 실행해야하는 경우 :

    xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)

    chmod여러 파일을 인수 로 허용하므로이 샘플에서는 필요하지 않지만 질문 제목과 일치합니다.

    특별한 경우에는 다음에 의해 생성 된 명령에서 파일 인수의 위치를 ​​정의 할 수도 있습니다 xargs.

    xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)

    seq 1 5입력으로 테스트

    이 시도:

    xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
    Blah 1 blabla 1..
    Blah 2 blabla 2..
    Blah 3 blabla 3..
    Blah 4 blabla 4..
    Blah 5 blabla 5..

    여기서 commande는 한 줄에 한 번 수행됩니다 .

  3. while read 및 변형.

    OP가 제안한 cat file.txt | while read in; do chmod 755 "$in"; done대로 작동하지만 두 가지 문제가 있습니다.

    • cat |입니다 쓸모 포크 ,와

    • | while ... ;done이후 환경이 붕괴 되는 서브 쉘 이 될 것 ;done입니다.

    그래서 이것은 더 잘 쓰여질 수 있습니다 :

    while read in; do chmod 755 "$in"; done < file.txt

    그러나,

    • 경고 $IFSread플래그 가 표시 될 수 있습니다 .

      help read
      read: read [-r] ... [-d delim] ... [name ...]
          ...
          Reads a single line from the standard input... The line is split
          into fields as with word splitting, and the first word is assigned
          to the first NAME, the second word to the second NAME, and so on...
          Only the characters found in $IFS are recognized as word delimiters.
          ...
          Options:
            ...
            -d delim   continue until the first character of DELIM is read, 
                       rather than newline
            ...
            -r do not allow backslashes to escape any characters
          ...
          Exit Status:
          The return code is zero, unless end-of-file is encountered...

      경우에 따라 사용해야 할 수도 있습니다

      while IFS= read -r in;do chmod 755 "$in";done <file.txt

      이상한 파일 이름 문제를 피하기 위해. 그리고 아마도 다음과 UTF-8같은 문제를 유발한다면 :

      while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
    • 을 ( STDIN를) 읽는 데 사용하는 동안 file.txt스크립트를 대화식으로 사용할 수 없습니다 ( STDIN더 이상 사용할 수 없음 ).

  4. while read -u전용을 사용 fd합니다.

    구문 : while read ...;done <file.txt로 리디렉션 STDIN됩니다 file.txt. 즉, 프로세스가 완료 될 때까지 프로세스를 처리 할 수 ​​없습니다.

    대화식 도구 를 작성하려는 경우 STDIN대체 파일 디스크립터 사용 을 피하고 사용해야 합니다 .

    상수 파일 설명은 다음과 같습니다 0에 대한 STDIN , 1대한 STDOUT2대한 STDERR . 당신은 그들을 통해 볼 수 있습니다 :

    ls -l /dev/fd/

    또는

    ls -l /proc/self/fd/

    거기에서 파일 설명 자로로 ( 0그리고 63실제로는 sysctl수퍼 유저 도구 에 따라) 사용되지 않는 숫자를 선택해야합니다 .

    이 데모에서는 fd 를 사용합니다 7.

    exec 7<file.txt      # Without spaces between `7` and `<`!
    ls -l /dev/fd/

    그런 다음 read -u 7이 방법을 사용할 수 있습니다 .

    while read -u 7 filename;do
        ans=;while [ -z "$ans" ];do
            read -p "Process file '$filename' (y/n)? " -sn1 foo
            [ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
        done
        if [ "$ans" = "y" ] ;then
            echo Yes
            echo "Processing '$filename'."
        else
            echo No
        fi
    done 7<file.txt

    done

    닫으려면 fd/7:

    exec 7<&-            # This will close file descriptor 7.
    ls -l /dev/fd/

    참고 : 병렬 프로세스로 많은 I / O를 수행 할 때이 구문이 유용 할 수 있기 때문에 파업 버전을 보냈습니다 .

    mkfifo sshfifo
    exec 7> >(ssh -t user@host sh >sshfifo)
    exec 6<sshfifo

3
으로이 xargs같은 필요성, 일부 기능의 이런 종류의 응답에 대한 initialy 빌드는 현재의 환경에서 가능한 한 오래 같이 명령을 구축 호출을 위해 chmod, 가능한 한 적은이 경우에 감소 포크 이용 효율을 보장합니다. while ;do..done <$file1 개 파일에 대해 1 개 포크 실행을 암시하십시오. xargs신뢰할 수있는 방식으로 1,000 개의 파일에 대해 1 포크를 실행할 수 있습니다.
F. Hauri

1
왜 세 번째 명령이 makefile에서 작동하지 않습니까? "예기치 않은 토큰`< '"근처에 구문 오류가 발생하지만 명령 행에서 바로 실행됩니다.
Woodrow Barlow

2
이것은 Makefile 특정 구문과 연결되어있는 것 같습니다. 명령 행을 되돌릴 수 있습니다 :cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
F. Hauri

어떤 이유로 @ F.Hauri는 tr \\n \\0 <file.txt |xargs -0 [command]설명 한 방법보다 약 50 % 빠릅니다.
phil294

2019 년 10 월, 새로운 편집, 대화식 파일 프로세서 샘플 추가
F. Hauri

150

예.

while read in; do chmod 755 "$in"; done < file.txt

이렇게하면 cat프로세스를 피할 수 있습니다 .

cat이 같은 목적으로 거의 항상 나쁘다. 쓸모없는 고양이 사용에 대해 더 읽어 볼 수 있습니다 .


을 피 하나는 cat 좋은 아이디어이지만,이 경우, 표시된 명령은xargs
F. 하우리

해당 링크가 관련이없는 것 같습니다. 아마도 웹 페이지의 내용이 변경 되었습니까? 답변의 나머지 부분은 굉장하지만 :)
starbeamrainbowlabs

@starbeamrainbowlabs 예. 페이지가 이동 한 것 같습니다. 나는 다시 연결되었고 지금은 괜찮을 것입니다. 감사합니다 :)
PP

1
감사! 이것은 특히 호출 이외의 작업을 수행해야하는 경우에 유용했습니다 chmod(예 : 실제로 파일의 각 행에 대해 하나의 명령을 실행).
Per Lundberg

백 슬래시에주의하십시오! 에서 unix.stackexchange.com/a/7561/28160 - " read -r(표준 입력에서 한 줄 읽어 read없이 -r당신이 원하지 않는, 해석의 백 슬래시)."
그 브라질 사람

16

좋은 선택 기가있는 경우 (예 : 디렉토리의 모든 .txt 파일) 다음을 수행 할 수 있습니다.

for i in *.txt; do chmod 755 "$i"; done

bash for 루프

또는 귀하의 변형 :

while read line; do chmod 755 "$line"; done <file.txt

작동하지 않는 것은 줄에 공백이 있으면 입력이 줄이 아니라 공백으로 나뉩니다.
Michael Fox

@Michael Fox : 구분 기호를 변경하여 공백이있는 줄을 지원할 수 있습니다. 줄 바꿈으로 변경하려면 스크립트 / 명령 전에 'IFS'환경 변수를 설정하십시오. 예 : 수출 IFS = '$ \ n'을
codesniffer

내 마지막 의견에 오타가 있습니다. 해야합니다 수출 IFS = $ '\ n'을
codesniffer

14

입력에 공백이 없다는 것을 알고 있다면 :

xargs chmod 755 < file.txt

경로에 공백이있을 수 있고 GNU xargs가있는 경우 :

tr '\n' '\0' < file.txt | xargs -0 chmod 755

나는 xargs에 대해 알고 있지만 (슬프게도) bash 내장 기능보다 while 및 read와 같은 안정적인 솔루션은 아닙니다. 또한 GNU xargs는 없지만 OS X를 사용하고 있으며 xargs에도 -0 옵션이 있습니다. 답변 해주셔서 감사합니다.
hawk

1
@ 호크 : xargs강하다. 이 도구는 매우 오래되었고 그의 코드는 강력하게 다시 방문했습니다 . 그의 목표는 셸 제한 (64kchar / line 또는 이와 유사한 것)과 관련하여 라인을 구축하는 것이 처음이었습니다. 이제이 도구는 매우 큰 파일로 작동 할 수 있으며 최종 명령에 대한 포크 수를 크게 줄일 수 있습니다. 내 답변 및 / 또는 참조하십시오 man xargs.
F. Hauri

@ 호크 어떤 방식으로 신뢰할 수있는 솔루션이 부족합니까? Linux, Mac / BSD 및 Windows에서 작동하는 경우 (예, MSYSGIT 번들은 GNU xargs 번들) 신뢰할 수 있습니다.
Camilo Martin

1
여전히 검색 결과에서 이것을 찾는 사람들은 Homebrew ( brew install findutils) 를 사용하여 macOS에 GNU xargs를 설치 한 다음 gxargs대신 대신 GNU xargs를 호출 할 수 있습니다.gxargs chmod 755 < file.txt
Jase

13

각 줄에 대해 명령을 병렬로 실행하려면 GNU Parallel을 사용할 수 있습니다

parallel -a <your file> <program>

파일의 각 줄은 인수로 프로그램에 전달됩니다. 기본적으로 parallelCPU 수만큼 스레드를 실행합니다. 그러나 당신은 그것을 지정할 수 있습니다-j


3

bash에 태그를 붙인 것을 보았지만 Perl도 좋은 방법입니다.

perl -p -e '`chmod 755 $_`' file.txt

정규식을 적용하여 올바른 파일을 얻도록 할 수 있습니다 (예 : .txt 파일 만 처리).

perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt

무슨 일이 일어나고 있는지 "미리보기"하기 위해서는 백틱을 큰 따옴표로 바꾸고 앞에 붙입니다 print.

perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt

2
왜 백틱을 사용합니까? 펄은 chmod기능을
glenn jackman

1
당신은 싶어 perl -lpe 'chmod 0755, $_' file.txt- 사용 -l은 "자동 씹는"기능에 대한
글렌 잭맨

1

파일을보다 유연하게 처리 할 수있는 AWK를 사용할 수도 있습니다.

awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt

파일에 다음과 같은 필드 구분 기호가있는 경우

field1, field2, field3

첫 번째 필드 만 얻으려면

awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt

GNU 설명서 https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple 에 대한 자세한 내용을 확인할 수 있습니다



0

늦었지만 그래도 알아

우연히을 \r\n대신 하여 Windows 저장 텍스트 파일을 실행할 \n경우 명령에 인수로 읽은 줄이 sth 인 경우 출력에 혼란이 생길 ​​수 있습니다. \r예를 들어 다음을 제거하십시오 .

cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.