xargs에서 파이프 출력을 올바르게 이스케이프합니다.

xargs에서 파이프 출력을 올바르게 이스케이프합니다.

예:

% touch -- safe-name -name-with-dash-prefix "name with space" \
    'name-with-double-quote"' "name-with-single-quote'" \
    'name-with-backslash\'

xargs큰따옴표를 처리할 수 없는 것 같습니다.

% ls | xargs ls -l 
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
ls: invalid option -- 'e'
Try 'ls --help' for more information.

해당 옵션을 사용하면 -0이름 앞에 대시가 붙으면 문제가 발생합니다.

% ls -- * | xargs -0 -- ls -l --
ls: invalid option -- 'e'
Try 'ls --help' for more information.

개행 문자, 제어 문자 등과 같이 잠재적으로 문제가 있는 다른 문자를 사용하기 전입니다.

답변1

이것POSIX 사양당신에게 예를 제공합니다 :

ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'

(파일 이름은 임의의 순서입니다.바이트( /NULL 제외) 및 sed/ xargs기대텍스트, 신뢰할 수 있도록 로케일을 C(NUL이 아닌 모든 바이트가 유효한 문자를 생성하는 경우)로 수정해야 합니다( xargs인수 최대 길이에 대한 제한이 매우 낮은 구현 제외).

-E ''일부 구현에서는 입력의 끝(예: 출력만) 을 나타내는 인수를 xargs이해해야 합니다 ._echo a _ b | xargsa

GNU를 사용하면 xargs다음을 사용할 수 있습니다.

ls | xargs -rd '\n' printf '<%s>\n'

(또한 추가된 -r(또한 GNU 확장)은 입력이 비어 있으면 명령이 실행되지 않는다는 것입니다.)

GNU에는 다른 구현에 의해 복사된 xargs것도 있으므로 다음과 같습니다.-0

ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'

휴대성이 조금 더 좋아졌습니다.

이 모든 것은 파일 이름에 개행 문자가 포함되어 있지 않다고 가정합니다. 파일 이름에 줄 바꿈이 있을 수 있는 경우 출력은 ls전혀 사후 처리되지 않습니다. 당신이 얻는 경우:

a
b

이것은 aand b파일일 수도 있고 이름이 이라는 파일일 수도 있습니다 a<newline>b. 알 수 있는 방법이 없습니다.

GNU에는 출력을 명확하게 만들고 사후 처리할 수 있는 ls기능이 있지만 인용은 예상 인용과 호환되지 않습니다 . 식별 및 인용 양식 . 그러나 and 는 둘 다 강한 따옴표이고 개행 문자를 포함할 수 없습니다( 개행 문자만 이스케이프할 수 있음 ). 따라서 강한 따옴표만 있고 개행 문자를 포함할 수 있지만 이스케이프 대신 줄 연속(제거)이 있는 sh 따옴표와 호환 되지 않습니다. 개행 문자.--quoting-style=shell-alwaysxargsxargs"..."\x'...'"..."'...'\xargs'...'\<newline>

셸을 사용하여 이 출력을 구문 분석하고 예상 형식으로 출력할 수 있습니다 xargs.

eval "files=($(ls --quoting-style=shell-always))"
[ "${#files[@]}" -eq 0 ] || printf '%s\0' "${files[@]}" |
  xargs -0 printf '<%s>\n'

또는 쉘이 파일 목록을 가져오고 NUL로 구분하여 전달하도록 할 수 있습니다 xargs. 예를 들면 다음과 같습니다.

  • 그리고 zsh:

    print -rNC1 -- *(N) | xargs -r0 printf '<%s>\n'
    
  • 그리고 ksh93:

    (set -- ~(N)*; (($# == 0)) || printf '%s\0' "$@") |
      xargs -r0 printf '<%s>\n'
    
  • 그리고 fish:

    begin set -l files *; string join0 -- $files; end |
      xargs -r0 printf '<%s>\n'
    
  • 그리고 bash:

    (
      shopt -s nullglob
      set -- *
      (($# == 0)) || printf '%s\0' "$@"
    ) | xargs -r0 printf '<%s>\n'
    

2023년 편집. GNU coreutils 버전 9.0(2021년 9월)부터 GNU에는 ls이제 --zero다음 명령과 함께 사용할 수 있는 옵션이 있습니다 xargs -r0.

ls --zero | xargs -r0 printf '<%s>\n'

답변2

xargsnull로 구분된 입력 옵션을 이해 하려면 -0보낸 사람은 보내는 데이터에 null 구분 기호도 적용해야 합니다.

그렇지 않으면 둘 사이에 동기화가 발생하지 않습니다.

한 가지 옵션은 GNU find이러한 구분 기호를 배치할 수 있는 명령입니다.

find . -maxdepth 1 ! -name . -print0 | xargs -0 ls -ld

답변3

말씀하신 대로 xargs를 사용하지 않는 한 일치하지 않는 큰따옴표를 좋아하지 마세요. -0하지만 -0이는 null로 끝나는 데이터를 입력하는 경우에만 의미가 있습니다. 따라서 이것은 실패합니다.

$ echo * | xargs
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
name-with-backslash -name-with-dash-prefix

하지만 이것은 작동합니다:

$ printf '%s\0' -- * | xargs -0
-- name-with-backslash\ -name-with-dash-prefix name-with-double-quote" name-with-single-quote' name with space safe-name

어쨌든 기본 접근 방식은 실제로 이 작업을 수행하는 가장 좋은 방법은 아닙니다. xargsand 및 ls이와 같은 것들을 조작하는 대신 쉘 glob을 사용하십시오.

$ for f in *; do ls -l -- "$f"; done
-rw-r--r-- 1 terdon terdon 4142 Aug 11 16:03 a
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-backslash\'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 -name-with-dash-prefix
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-double-quote"'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 "name-with-single-quote'"
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name with space'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 safe-name

답변4

이것을 시도하는 것은 매우 어리석은 일입니다명령의 출력을 구문 분석합니다.ls그건구문 분석용으로 설계되지 않음명령을 내리다여러 문자를 처리하는 데 적합하지 않음(예: new lines{}) 쉘이 자체적으로 이 작업을 수행하는 경우:

set -- *; for f; do echo "<$f>"; done

set    -- *
for    f
do     ls "$f"
done

또는 명령줄에서 다음을 수행합니다.

$ set -- *; for f; do echo "<$f>"; done
<name-with-backslash\>
<-name-with-dash-prefix>
<name-with-double-quote">
<name-with-single-quote'>
<name with space>
<safe-name>
<with_a
newline>

출력 처리(그리고 마지막 파일 이름으로 n 개의 예제를 갖는 것)는 개행에 전혀 문제가 없다는 점에 유의하십시오.

또는 파일 개수로 인해 셸 속도가 느려지는 경우 find를 사용하세요.

$ find ./ -type f -exec echo '<{}>' \;
<./safe-name>
<./with_a
newline>
<./name-with-double-quote">
<./-name-with-dash-prefix>
<./name with space>
<./name-with-single-quote'>
<./name-with-backslash\>

find는 쉘과 다르게 모든 도트 파일과 모든 하위 디렉터리를 처리합니다.

관련 정보