공백으로 가능한 파일 이름 목록으로 작업하는 POSIX 호환 방법 수 없으므로 공백을 포함 할

공백을 포함하는 파일 이름으로 작업하기 위해 배열 사용을 제안하는 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


답변

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.

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


답변

$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을 반복 하는 데 사용합니다.