programing

공백이 있는 파일 목록에서 반복

goodcopy 2023. 4. 8. 11:47
반응형

공백이 있는 파일 목록에서 반복

파일 목록을 반복하고 싶습니다.는 this이리 this this this this this this this this this this this this 의 결과입니다.find그래서 생각해낸 건

getlist() {
  for f in $(find . -iname "foo*")
  do
    echo "File found: $f"
    # do something useful
  done
}

파일 이름에 공백이 있는 경우를 제외하고는 문제 없습니다.

$ ls
foo_bar_baz.txt
foo bar baz.txt

$ getlist
File found: foo_bar_baz.txt
File found: foo
File found: bar
File found: baz.txt

공간 분할을 방지하려면 어떻게 해야 합니까?

단어 기반 반복을 줄 기반 반복으로 대체할 수 있습니다.

find . -iname "foo*" | while read f
do
    # ... loop body
done

이를 달성하기 위한 몇 가지 실행 가능한 방법이 있습니다.

원래 버전을 고수하고 싶다면 다음과 같이 할 수 있습니다.

getlist() {
        IFS=$'\n'
        for file in $(find . -iname 'foo*') ; do
                printf 'File found: %s\n' "$file"
        done
}

파일 이름에 리터럴 줄바꿈이 포함되어 있어도 공백으로 구분되지 않는 경우에도 이 작업은 실패합니다.

하지만, IFS를 가지고 장난칠 필요는 없습니다.이를 위해 제가 선호하는 방법은 다음과 같습니다.

getlist() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: %s\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

「」를 < <(command)구문이 익숙하지 않은 경우 프로세스 대체에 대해 읽어보십시오.이 방법의 장점은for file in $(find ...)공백, 줄바꿈 및 기타 문자가 포함된 파일이 올바르게 처리되는 것입니다. 것은 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 맞다.'find-print0 쓰겠습니다.null 명kaka (\0각 파일명의 터미네이터로서 null은 줄바꿈과 달리 파일명의 정규문자가 아닙니다.

거의 동등한 버전에 비해 이 기능의 장점

getlist() {
        find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
                printf 'File found: %s\n' "$file"
        done
}

한편, loop으로 동작합니다.로 to에 하면 이이 that that that that that that thatwhilewhile당신이 원하는 게 아닐 수도 있어요

이 '''에 비해 점''find ... -print0 | xargs -0★★★★xargs필요한 것은 한 줄 인쇄 또는 파일에 대한 단일 조작뿐이지만 여러 단계를 수행해야 하는 경우에는 루프 버전이 더 쉽습니다.

편집: 이 문제를 해결하기 위한 다양한 시도 간의 차이를 알 수 있는 좋은 테스트 스크립트를 소개합니다.

#!/usr/bin/env bash

dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"

touch       'file not starting foo' foo foobar barfoo 'foo with spaces'\
    'foo with'$'\n'newline 'foo with trailing whitespace      '

# while with process substitution, null terminated, empty IFS
getlist0() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# while with process substitution, null terminated, default IFS
getlist1() {
    while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# pipe to while, newline terminated
getlist2() {
    find . -iname 'foo*' | while read -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# pipe to while, null terminated
getlist3() {
    find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, default IFS
getlist4() {
    for file in "$(find . -iname 'foo*')" ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, newline IFS
getlist5() {
    IFS=$'\n'
    for file in $(find . -iname 'foo*') ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}


# see how they run
for n in {0..5} ; do
    printf '\n\ngetlist%d:\n' $n
    eval getlist$n
done

rm -rf "$dir"

또한 매우 간단한 해결책이 있습니다: bash globing에 의존합니다.

$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid   file 3"
$ ls
stupid   file 3  stupid file1     stupid file2
$ for file in *; do echo "file: '${file}'"; done
file: 'stupid   file 3'
file: 'stupid file1'
file: 'stupid file2'

이 동작이 디폴트 동작인지는 모르겠지만, 제 숍에는 특별한 설정이 없기 때문에, 「안전」하다고 말할 수 있습니다(osx와 ubuntu로 테스트).

find . -iname "foo*" -print0 | xargs -L1 -0 echo "File found:"
find . -name "fo*" -print0 | xargs -0 ls -l

것은, 을 참조하십시오.man xargs.

의 필터링을 하지 때문에find, 현재 다음 항목을 사용할 수 있습니다.bash4.0:

shopt -s globstar
getlist() {
    for f in **/foo*
    do
        echo "File found: $f"
        # do something useful
    done
}

**/는 0 에 풀 은 0과 합니다.foo*현재 디렉토리 또는 하위 디렉토리로 이동합니다.

저는 루프와 어레이 반복을 매우 좋아하기 때문에 이 답변을 혼합에 추가하려고 합니다.

나는 또한 마셸블링의 멍청한 파일 예시를 좋아했다.:)

$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid   file 3"

테스트 디렉토리 내부:

readarray -t arr <<< "`ls -A1`"

파일 각 행이 ""라는 이름의 됩니다.arr후행 줄바꿈을 모두 제거합니다.

이 파일에 더 좋은 이름을 붙이고 싶다고 칩시다.

for i in ${!arr[@]}
do 
    newname=`echo "${arr[$i]}" | sed 's/stupid/smarter/; s/  */_/g'`; 
    mv "${arr[$i]}" "$newname"
done

${!arr[@]}이(가) 0 1 2로 확장되므로 "${arr[$i]}"이(가) 어레이의 ith 요소가 됩니다.변수 주변의 따옴표는 공간을 보존하기 위해 중요합니다.

그 결과, 다음의 3개의 파일명이 변경됩니다.

$ ls -1
smarter_file1
smarter_file2
smarter_file_3

find 있다-exec검색 결과를 루프하고 임의 명령을 실행하는 인수입니다.예를 들어 다음과 같습니다.

find . -iname "foo*" -exec echo "File found: {}" \;

서 ★★★★{}는 발견된 파일을 , 이 을 「파일을 검색해 주세요」, 「파일을 검색해 주세요」, 「파일을 검색해 주세요」, 「파일을 검색해 주세요.""그럼 결과 셸 명령어를 사용하여 파일 이름 내의 공백을 처리할 수 있습니다.

할 수 .\;명령어를 는 (새 명령어를 시작합니다.)와 함께 합니다.\+ 1개의 커맨드에 모두 ).man find세한것 、 을을해해요요 。

최근에 비슷한 사건을 다뤄야 했고FILES「 」 「 」 、 「 」 。

eval FILES=($(find . -iname "foo*" -printf '"%p" '))

서는 각 합니다.FILES .의 사용eval는, 「」의합니다.find어레이 초기화를 위해 올바르게 출력합니다.

파일을 반복하려면 다음 작업을 수행합니다.

for f in "${FILES[@]}"; do
    # Do something with $f
done

경우에 따라서는 파일 목록을 복사하거나 이동하기만 하면 해당 목록을 awk로 파이핑할 수도 있습니다.
: ★★★★★\"" "\"을 돌다$0(예, 예, 예, 예, 예, 예 = 예)

find . -iname "foo*" | awk '{print "mv \""$0"\" ./MyDir2" | "sh" }'

OK - Stack Overflow에 대한 첫 번째 게시물!

이것에 관한 나의 문제는 항상 csh에 있는 것이었지만, 내가 제시하는 솔루션은 양쪽에서 효과가 있을 것이라고 확신한다.'ls'는 'ls'는 'ls'가 아니라 'ls'가 됩니다.「ls」의 셸 을 사용하면, 「삭제할 수 .*- 된 폴더 " match"이를 회피하기 을 확장합니다.따라서 "no match"는 "no match"입니다.이 문제를 해결하려면 닷 파일을 포함하도록 확장을 확장하기만 하면 됩니다.* .*파일이 항상 존재하기 때문에 항상 결과를 얻을 수 있습니다.할 수 . csh 에서는요.

foreach file (* .*)
   echo $file
end

표준 닷 파일을 필터링 하고 싶다면, 그것은 충분히 간단합니다.

foreach file (* .*)
   if ("$file" == .) continue
   if ("file" == ..) continue
   echo $file
end

이 스레드의 첫 번째 게시물의 코드는 다음과 같이 기술됩니다.

getlist() {
  for f in $(* .*)
  do
    echo "File found: $f"
    # do something useful
  done
}

이게 도움이 됐으면 좋겠네요!

작업에 대한 또 다른 솔루션...

목표는 다음과 같습니다.

  • 디렉토리에서 파일 이름을 재귀적으로 선택/필터링하다
  • 각 이름 처리(경로 내 공백 없음)
#!/bin/bash  -e
## @Trick in order handle File with space in their path...
OLD_IFS=${IFS}
IFS=$'\n'
files=($(find ${INPUT_DIR} -type f -name "*.md"))
for filename in ${files[*]}
do
      # do your stuff
      #  ....
done
IFS=${OLD_IFS}


언급URL : https://stackoverflow.com/questions/7039130/iterate-over-a-list-of-files-with-spaces

반응형