연속 명령에서 파일 이름을 별도의 인수로 유지합니다.

연속 명령에서 파일 이름을 별도의 인수로 유지합니다.

면책조항: 이아니요ls 또는 grep과 같은 특정 명령과 관련되어 있지만 예제에서는 사용했지만 이는 bash에 더 가깝습니다.

"특정 단어"가 모두 포함된 파일 집합(ls -l)의 파일 크기, 타임스탬프 및 권한/소유자/그룹을 보고 싶다고 가정해 보겠습니다. 보통 나는 이렇게 한다:

ls -l $( grep -l 'a certain word' * )

이는 "filename"이라는 파일과 같이 파일 이름에 공백이 포함될 때까지 마술처럼 작동합니다. 그러면 ls는 두 개의 파일 이름인 "file"과 "name"을 찾을 수 없습니다.

Bash 스크립트에는 이 문제의 변형을 해결하는 친숙한 방법이 있습니다. 명령줄에서 파일 이름을 스크립트에 전달하면 ${1}, ${2} 등으로 인용됩니다. "${1}", "${2}"와 같이 따옴표로 묶으면 문제가 없으며 공백이 있어도 처리할 수 있습니다.모두"${@}"를 사용하여 파일 이름을 즉시 표시하면 각 매개변수가 올바르게 나열되고 공백이 있어도 그대로 유지됩니다. 나열한 grep/ls 스타일 문제를 해결할 수 있는 메커니즘을 찾지 못했습니다. 이상. 확장 명령을 따옴표로 묶으면 $( )모든 파일을 ls에 대한 하나의 큰 인수로 처리됩니다.

나는 보통 이런 식으로 이 문제를 해결하겠지만 grep -l .... | while read file; do ls -l "${file}"; done, 정말 그렇게 해서는 안 됩니다. 이렇게 해도 날짜/크기/기타(예 ls -ltr $( grep -l ....):) 별로 파일을 정렬할 수 없습니다.

나는 항상 bash에 대한 해결책이 있다고 생각했지만 아직 그것을 접하지 못했습니다. 이 문제는 가끔씩만 나타나서 보통 고치지만 지금은 방법이 있을 것이라고 생각할 정도로 나를 괴롭힙니다. .

예, 다음을 보았고 사용 중인 특정 도구(예: tar)로 해결된 문제이거나 해당 문제가 명령에 따라 다르기 때문에 대답은 "죄송합니다. 여기서는 거기까지 갈 수 없습니다"입니다. 이 문제를 해결하기 위해 더 많은 시간을 보내거나 마법을 찾는 대신 공백이 포함된 결과로 제공된 파일 이름을 처리하고 이를 다른 명령에 인수로 전달하도록 요청합니다. 나는 동일하지 않은 질문을 제공하는 것보다 이 질문에 답이 없는 상태(해결책이 없음을 증명하는 것)를 선호합니다.

답변1

잘. @don_crissti가 이미 답변을 제공했습니다.grep의 경우댓글에. 그러나 이것이 실제로 grepOR 에 관한 것이 아니라고 말씀하셨으므로 ls해당 명령을 사용하지 않도록 문제의 명령을 다시 작성하겠습니다.

내 생각에 당신이 원하는 것은 다음과 같습니다.

do-something-with $(produce-list-of-files)

명령 중 하나의 출력은 다른 명령에 대한 명령줄 인수로 입력되어야 합니다. 이 목적을 위한 유틸리티가 있는데, 이를 호출합니다.xargs (맨 페이지).

파일 이름이 "good"이면 이렇게 할 수 있습니다.

produce-list-of-files | xargs do-something-with

xargs파일 이름에 공백이 포함될 수 있지만 줄 바꿈으로 구분될 수 있는 경우 분할하지 말라고 알려야 합니다.어느공백(개행만 가능):

produce-list-of-files | xargs -d '\n' do-something-with

파일 이름에 개행 문자도 포함될 수 있는 경우 목록은 NUL('\0', 값이 0인 바이트)로 구분되어야 하며 xargs이를 지원하는 파일 이름이 필요합니다. 다양한 유틸리티 중 적어도 일부 버전은 개행 문자 대신 NUL로 구분된 파일 나열을 지원하며, 이러한 도구의 GNU 버전에서는 적어도 find -print0, sort -z및 . 기호 grep -Z는 또는입니다. 그래서:xargs--null-0

produce-list-of-files -0 | xargs -0 do-something-with

cat및를 사용하여 실행하는 예 ls -l:

$ touch "abba acdc" "foo bar" $'new\nline'
$ echo -en "abba acdc\0foo bar\0new\nline\0" > list
$ cat list | xargs -0 ls -l
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 abba acdc
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 foo bar
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 new?line

답변2

줄 바꿈으로 구분된 항목 목록(예: 파일 이름)을 생성하는 명령이 있고 항목 자체에 줄 바꿈이 포함될 수 있는 경우 여기에서 해당 항목으로 이동할 수 없습니다.

grep -Z일부 시스템의 일부 명령은 GNU grep( ), GNU sed( sed -z), GNU awk( gawk RS='\0' ORS='\0'), GNU 및 BSD find( ) 등과 같이 줄 바꿈 대신 널 바이트로 구분된 목록을 생성하거나 관리할 수 있습니다. 그러면 다음을 find -print0사용할 수 있습니다. xargs -0빈으로 구분된 항목 목록에서 명령을 실행합니다.

find … -print0 | sed -z … | xargs -0 frob …

파일 이름에 개행 문자가 포함되어 있지 않다고 가정하려는 경우 분할+glob 연산자(즉, 따옴표 없이 확장)를 사용하여 개행으로 구분된 목록을 분할할 수 있습니다. 고빙을 끄고 개행 문자에서 분할(변수) set -f하도록 구분 기호를 설정 해야 합니다.IFS

IFS='
'; set -f
ls -l -- $(grep -l 'a certain word' *)
set +f; unset IFS

마지막 줄은 기본 설정을 복원합니다. 설정을 정확하게 저장하고 set -f복원하는 IFS것은 가능하지만 번거롭습니다.

나중에 처리하기 위해 출력을 저장해야 하는 경우 출력을 정의하고 데이터를 배열에 저장할 때 분할하거나 사용할 때 분할하여 데이터를 문자열에 저장할 수 있습니다. 배열 사용(ksh, bash 또는 zsh 필요):

IFS='
'; set -f
files=($(grep -l 'a certain word' *))
set +f; unset IFS
ls -l -- "${files[@]}"

문자열을 사용하세요:

files=$(grep -l 'a certain word' *)
IFS='
'; set -f
ls -l -- $files
set +f; unset IFS

당신은 또한 볼 수 있습니다공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?

관련 정보