공백으로 가능한 파일 이름 목록으로 작업하는 POSIX 호환 방법


14

공백을 포함하는 파일 이름으로 작업하기 위해 배열 사용을 제안하는 Bash 스크립팅 안내서를 보았습니다. 그러나 DashAsBinSh 는 배열을 이식 할 수 없으므로 공백을 포함 할 수있는 파일 이름 목록으로 작업하는 POSIX 호환 방법을 찾고 있습니다.

아래 예제 스크립트를 수정하려고합니다. echo

foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar

여기 스크립트가 있습니다

#!/usr/bin/env sh

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps

dostuffwith() { echo $1; };

F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)

for f in $ALL_FILES
do
    fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
    dostuffwith $fpath
done

답변:


8

POSIX 포탄 하나 개의 어레이를 가지고 위치 매개 변수 ( $1, $2, 등의 총칭에 대해 참조 "$@").

set -- 'foo/target/a.jar' 'foo/target/b.jar' 'bar/target/b.jar' 'bar/target/lol whitespace.jar'
set -- "$@" '/another/one at the end.jar'

for jar do
  dostuffwith "$jar"
done

이것은 하나만 있기 때문에 불편하며 위치 매개 변수의 다른 사용을 파괴합니다. 위치 매개 변수는 함수에 국한되며 때로는 축복이며 때로는 저주입니다.

파일 이름에 줄 바꿈이 포함되어 있지 않은 경우 줄 바꿈을 구분 기호로 사용할 수 있습니다. 변수를 확장 할 때는 먼저 글 로빙을 끄고 set -f필드 분할 문자 목록에 IFS줄 바꾸기 만 포함되도록 설정하십시오 .

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

set -f; IFS='
'                           # turn off variable value expansion except for splitting at newlines
for jar in $INPUT; do
  set +f; unset IFS
  dostuffwith "$jar"        # restore globbing and field splitting at all whitespace
done
set +f; unset IFS           # do it again in case $INPUT was empty

목록의 항목을 개행 문자로 구분하면 특히 많은 텍스트 처리 명령을 유용하게 사용할 수 있습니다 sort.

명시 적으로 필드 분할을 원할 때를 제외하고 (글로브를 끄지 않는 한 글 로빙하는 경우를 제외하고) 변수 대체에 큰 따옴표를 항상 사용해야합니다.


좋은 답변과 설명. 원래 sort | uniq단계가 의도 한대로 작동 하기 때문에 이것을 허용 된 것으로 표시하겠습니다 .
Eero Aaltonen

5

$INPUT변수는 개행 문자를 구분 기호로 사용 하기 때문에 파일 이름에 개행 문자가 없다고 가정합니다. 따라서 파일을 반복하고 공백을 유지하는 간단한 방법이 있습니다.

아이디어는 read쉘 내장 을 사용하는 것입니다 . 일반적으로 read공백이 분리되므로 공백이 끊어집니다. 그러나 설정할 수 있으며 IFS=$'\n'대신 줄 바꿈에서만 나뉩니다. 따라서 목록의 각 줄을 반복 할 수 있습니다.

내가 얻을 수있는 가장 작은 솔루션은 다음과 같습니다.

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

dostuffwith() {
    echo "$1"
}

echo "$INPUT" | awk -F/ '{if (!seen[$NF]++) print }' | \
while IFS=$'\n' read file; do
  dostuffwith "$file"
done

기본적으로 awk파일 이름을 기준으로 중복 제거되는 "$ INPUT"을 보냅니다 (분할 /한 다음 마지막 항목을 이전에 보지 않은 경우 줄을 인쇄 함). 그런 다음 awk가 파일 경로 목록을 생성하면 목록 while read을 반복 하는 데 사용합니다.


$ checkbashisms bar.sh bar.sh 줄 14에서 가능한 bashism (<<< here string)
Eero Aaltonen

1
@EeroAaltonen herestring을 사용하지 않도록 변경되었습니다. 그러나이 변경으로 while루프 dostuffwith가 서브 쉘에서 실행됩니다. 따라서 루프가 완료되면 실행중인 쉘에 대한 모든 변수 또는 변경 사항이 손실됩니다. 유일한 대안은 전체 heredoc을 사용하는 것입니다.이 불쾌하지는 않지만 이것이 바람직하다고 생각했습니다.
Patrick

작은 것보다 가독성에 더 많은 포인트를 부여하고 있습니다. 이것은 확실히 작동하며 이미 +1입니다.
Eero Aaltonen

IFS="\n"백 슬래시와 n 문자로 분할합니다. 그러나 read file에는 분할이 없습니다. IFS="\n"입력의 시작과 끝에서 제거되었을 $ IFS에서 공백 문자를 제거한다는 점에서 여전히 유용합니다. 선을 읽으려면, 정규 구문은 IFS= read -r line하지만, IFS=anything read -r line(제공 아무것도 공백을 포함하지 않습니다) 잘 작동합니다.
Stéphane Chazelas

죄송합니다. 내가 어떻게 관리했는지 잘 모르겠습니다. 결정된.
Patrick
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.