나는 이 링크 를 인용하여 "파싱하지 마라 ls
!" 이것은 몇 가지 이유로 나를 귀찮게합니다.
그 링크의 정보는 약간의 질문으로 도매로 받아 들여졌지만 캐주얼 독서에서 최소한 몇 가지 오류를 선택할 수는 있습니다.
또한 그 링크에 명시된 문제가 해결책을 찾고자하는 욕구를 불러 일으키지 않은 것처럼 보입니다.
첫 번째 단락에서 :
...
[ls]
파일 목록 을 요청할 때 큰 문제가 있습니다. 유닉스는 공백 문자, 줄 바꿈, 쉼표, 파이프 기호 및 거의 모든 다른 문자를 포함하여 파일 이름에 거의 모든 문자를 허용합니다. NUL을 제외한 분리 문자. ...ls
파일 이름을 줄 바꿈으로 구분합니다. 이름에 줄 바꿈이있는 파일이 생길 때까지 괜찮습니다. 그리고 줄ls
바꿈 대신 NUL 문자로 파일 이름을 종료 할 수 있는 구현 방법을 모르므로으로 파일 이름 목록을 안전하게 가져올 수 없습니다ls
.
버머 맞지? 어떻게 지금까지 우리는 개행 문자가 개행 문자를 포함 할 수있는 데이터에 대한 상장 데이터 집합을 종결 처리 할 수 있습니까? 글쎄,이 웹 사이트에서 질문에 대답하는 사람들이 매일 이런 종류의 일을하지 않았다면, 우리가 어려움을 겪고 있다고 생각할 수 있습니다.
사실 대부분의 ls
구현은 실제로 출력을 구문 분석하기 위해 매우 간단한 API를 제공하며 우리는 그것을 실현하지 않고도 모든 작업을 수행했습니다. 파일 이름을 null로 끝낼 수있을뿐만 아니라 null을 사용하거나 원하는 다른 임의의 문자열로 파일 이름을 시작할 수 있습니다. 또한 파일 유형별로 이러한 임의의 문자열을 할당 할 수 있습니다 . 고려하십시오 :
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
자세한 내용은 이것을 참조하십시오 .
이제이 기사의 다음 부분은 실제로 나를 이해시켜줍니다.
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
문제는의 출력에서
ls
사용자 또는 컴퓨터가 파일 이름을 구성하는 부분을 말할 수 없다는 것 입니다. 각 단어입니까? 아니요. 각 줄입니까? 아닙니다.이 질문에 대한 정답은 없습니다 : 당신은 말할 수 없습니다.또한
ls
파일 이름 데이터를 때때로 깨뜨리는 방법에 주목 하십시오 (이 경우 "a" 와 "newline"\n
사이의 문자를 ? 물음표 로 바꿨습니다 . ......
현재 디렉토리의 모든 파일을 반복하려면
for
루프와 glob를 사용하십시오.
for f in *; do
[[ -e $f ]] || continue
...
done
저자는이 호출 파일 이름을 잘못 전달 되면 ls
쉘 globs와를 포함하는 파일 이름의 목록을 반환 한 후와 파일 목록을 검색 쉘 글로브를 사용하는 것이 좋습니다!
다음을 고려하세요:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX는 피연산자를 다음 -1
과 같이 정의 합니다 -q
ls
.
-q
-인쇄 할 수없는 파일 이름 문자 및<tab>
s의 각 인스턴스를 물음표 ('?'
) 문자로 작성하십시오. 출력이 터미널 장치에 대한 경우 구현시 기본적으로이 옵션을 제공 할 수 있습니다.
-1
- (숫자 1) 출력이 한 줄에 하나씩 입력되도록합니다.
글 로빙에는 고유 한 문제가 없습니다. 어떤 문자 ?
와 도 일치 하므로 ?
목록에서 여러 개의 일치하는 결과가 동일한 파일과 여러 번 일치합니다. 그것은 쉽게 처리됩니다.
이 작업을 수행하는 방법이 요점은 아니지만 결국에는 많은 시간이 걸리지 않으며 아래에 설명되어 있습니다 . 이유 에 관심이있었습니다 . 내가 생각할 때, 그 질문에 대한 가장 좋은 대답이 받아 들여졌습니다. 사람들이 할 수없는 것보다 할 수 있는 일을 사람들에게 알리는 데 더 자주 집중하는 것이 좋습니다 . 내가 생각 하듯이 적어도 당신은 틀린 것으로 증명 될 가능성이 훨씬 낮습니다.
그러나 왜 시도조차합니까? 분명히, 나의 주된 동기는 다른 사람들이 나에게 할 수 없다고 계속 말하고 있다는 것이었다. 나는 ls
당신이 무엇을 찾아야 하는지를 알기 만하면 출력이 규칙적이고 예측 가능하다는 것을 잘 알고 있습니다. 잘못된 정보는 대부분의 일보다 나를 귀찮게합니다.
그러나 Patrick과 Wumpus Q. Wumbley의 답변 (후자의 멋진 핸들에도 불구하고)을 제외하고는 사실을 제외 하고는 대답 의 대부분의 정보가 대부분 올바른 것으로 간주합니다. 일반적으로 구문 분석하는 것보다 현재 디렉토리를 검색 할 때 더 효과적 ls
입니다. 그들은 내 점에서 적어도 충분한 이유 중 하나는 위의 기사에서 인용 한 잘못된 정보를 전파하거나 그들이 수용 할 명분 있습니다 정당화하기 위해, 그러나 아니다 " 구문 분석되지 않습니다 ls
. "
Patrick의 답변의 일관되지 않은 결과는 주로 zsh
then을 사용한 결과입니다 bash
. zsh
-기본적으로- 이식 가능한 방식으로 $(
대체 단어를 단어 분리하지 않습니다 )
. 그래서 그가 나머지 파일들은 어디로 갔냐 고 물었을 때 ? 그 질문에 대한 대답은 당신의 껍질이 그들을 먹었다는 것입니다. 휴대용 쉘 코드를 SH_WORD_SPLIT
사용 zsh
하고 처리 할 때 변수 를 설정해야하는 이유가 여기에 있습니다. 나는 그의 답변에서 이것을 지적하지 못한 것이 끔찍한 오도라고 생각합니다.
Wumpus의 대답은 나를 위해 계산되지 않습니다. 목록 컨텍스트에서 ?
캐릭터 는 쉘 글로브입니다. 다른 말을하는 방법을 모르겠습니다.
여러 결과 사례를 처리하려면 글로브의 욕심을 제한해야합니다. 다음은 끔찍한 파일 이름의 테스트 기반을 만들고 표시합니다.
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
산출
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
지금 나는거야 안전한 아닌 모든 문자 /slash
, -dash
, :colon
다음 쉘 글로브에서, 또는 알파 - 숫자 문자 sort -u
의 고유 결과에 대한 목록입니다. ls
인쇄 할 수없는 문자는 이미 보호 되었으므로 안전합니다. 손목 시계:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
산출:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
아래에서 문제에 다시 접근하지만 다른 방법론을 사용합니다. \0
null 이외 의 /
ASCII 문자는 경로명에서 금지 된 유일한 바이트입니다. 여기에 globs를두고 대신 POSIX 지정 -d
옵션 ls
과 POSIX 지정 -exec $cmd {} +
구성을 결합 find
합니다. 때문에 find
오직 자연스럽게 방출합니다 /
순서를은 쉽게 다음은 각 항목에 대한 모든 dentry 정보를 포함 재귀 안정적으로 구분 된 파일 목록을 조달. 다음과 같이 무엇을 할 수 있는지 상상해보십시오.
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
특히 결과 고유성이 문제가 될 때 매우 유용 할 수 있습니다.
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
이것들은 내가 생각할 수있는 가장 휴대용 수단입니다. GNU ls
를 사용하면 다음을 수행 할 수 있습니다.
ls --quoting-style=WORD
마지막으로, inode 번호가 필요할 때 자주 사용 하는 훨씬 간단한 구문 분석ls
방법이 있습니다.
ls -1iq | grep -o '^ *[0-9]*'
그것은 단지 inode 번호를 반환합니다-이것은 또 다른 편리한 POSIX 지정 옵션입니다.
stat
실제로 각 파일이 존재하는지 확인하기 때문에 대답 에 사용하고 있는 이유 입니다. sed
물건 의 맨 아래에있는 비트 가 작동하지 않습니다.
ls
처음에 파싱하지 않는 것보다 질문이 설명하는 모든 농구 대를 뛰어 넘는 것이 더 쉽고 간단하거나 어떤 식 으로든 더 나은 방법 은 무엇입니까? 당신이 묘사하는 것은 매우 어렵습니다. 나는 그것을 이해하기 위해 그것을 분해해야하며 상대적으로 유능한 사용자입니다. 평범한 Joe가 이와 같은 것을 처리 할 수있을 것으로 기대할 수 없습니다.
ls
출력 구문 분석 이 잘못된 모든 이유 는 원래 링크 (및 기타 여러 위치)에서 잘 설명되었습니다. OP가이를 이해하는 데 도움을 요청했다면이 질문은 합리적이었을 것입니다. 그러나 OP는 단순히 자신의 잘못된 사용법이 괜찮다는 것을 증명하려고합니다.
parsing ls is bad
. for something in $(command)
정확한 결과를 얻기 위해 단어 분리에 의존하고 의존하는 것은 대부분 command's
간단한 결과를 얻지 못하는 사람들에게는 좋지 않습니다.
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 대 3.18stime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
=의 1.28s