병렬 및 공백이 있는 ls

병렬 및 공백이 있는 ls

이 명령은 "일반" 이름이 dir1 mydir my-dir 등인 디렉토리에 대해 작동합니다.

ls | parallel 'echo -n {}" "; ls {}|wc -l'

각 디렉토리의 파일 수를 알려주세요.

그러나 공백이 있는 디렉터리(예: "내 디렉터리" 또는 긴 디렉터리 이름)의 경우 작동하지 않고 오류가 발생합니다.

공백을 인용/이스케이프하는 방법은 무엇입니까?

답변1

GNU Parallel은 공백을 처리하는 데 문제가 없습니다.

$ mkdir 'a  b'
$ touch 'a  b/c  d'
$ ls | parallel 'echo -n {}" "; ls {}|wc -l'
a  b 1

-0이름에 \n이 포함되어 있으면 다음이 필요합니다.

$ mkdir 'a

b'
$ touch 'a

b/c

d'
# fails
$ ls | parallel 'echo -n {}" "; ls {}|wc -l'
$ parallel 'echo -n {}" "; ls {}|wc -l' ::: *
# works
$ printf "%s\0" * | parallel -0 'echo -n {}" "; ls {}|wc -l'
$ parallel -0 'echo -n {}" "; ls {}|wc -l' ::: *

ls따라서 여러분이 보고 있는 것은 아마도 이상한 일을 했기 때문일 것입니다 ls --some-weird-option. 대신 시도해 보십시오 ( 또는 위에 표시된 대로 \ls사용 ).printf ... | ... -0-0 ... ::: *

\ls | parallel 'echo -n {}" "; ls {}|wc -l'

(추신: 알고 계십니까 --tag?

parallel --tag 'ls {}|wc -l' ::: *

)

답변2

parallel아니요, xargs또는 경로 이름을 앞뒤로 전달하는 복잡성:

shopt -s nullglob dotglob

for dir in */; do
    set -- "$dir"/*
    printf '%s:\t%s\n' "${dir%/}" "$#"
done

*즉, 모든 디렉터리를 반복하고 각 디렉터리에서 전역 확장 이름의 수를 계산합니다.

위의 경우 일치하지 않는 패턴이 확장되지 않은 채로 남아 있는 것이 아니라 제거되도록 셸 옵션을 bash설정했습니다 . nullglob또한 dotglob숨겨진 이름과 일치하도록 쉘 옵션을 설정했습니다.

쉘은 zsh디렉토리(루프용)와 일반 파일(루프 본문)의 전역 일치를 필터링하는 동안 이 작업을 수행할 수 있습니다. 아래 코드에서 glob 한정자는 (ND/)이전 콘텐츠가 *shell 과 동일한 효과를 갖고 설정된 디렉터리에만 일치하도록 하며 nullglob, 동일한 방식으로 이전 콘텐츠가 일반 파일에만 일치하도록 합니다.dotglobbash(ND.)*

for dir in *(ND/); do
    set -- $dir/*(ND.)
    printf '%s:\t%s\n' $dir $#
done

너 이거 하고 싶어?재귀적으로, 계층 구조의 각 디렉터리에 있는 이름 수를 얻으려면 위의 내용을 삽입합니다 find.

find . -type d -exec bash -O nullglob -O dotglob -c '
    for dir do
        set -- "$dir"/*
        printf "%s:\t%s\n" "$dir" "$#"
    done' bash {} +

bash(위의 내용은 이 답변의 시작 부분에 있는 일반 루프와 약간 다릅니다. 이는 심볼릭 링크를 통해 액세스된 디렉토리의 이름을 계산하지 않기 때문입니다.) 또는,

find . -type d -exec zsh -c '
    for dir do
        set -- $dir/*(ND.)
        printf "%s:\t%s\n" $dir $#
    done' zsh {} +

답변3

cmd가 실행되는 하위 디렉터리에 있는 일반적인 파일 수를 나열하려고 한다고 가정하면 다음 코드 줄이 이를 수행합니다.

 $ find . -maxdepth 1 -type d ! -name "." -print0 2>/dev/null \
   | xargs -0 -I {} sh -c 'printf "%20s:  %d\n" "{}" "$(find "{}" -maxdepth 1 -type f 2>/dev/null| wc -l)"'

출력 예:

              ./Maildir:  0
             ./.dvisvgm:  0
               ./.pyenv:  5
             ./.ipython:  0
   ./.ipynb_checkpoints:  3
                ./.tmux:  1
         ./.virtualenvs:  12
         ./seaborn-data:  2
               ./.local:  2
                ./bgpix:  12
                 ./.vim:  7
     ...
  • 2>/dev/nullfind테스트를 실행하는 데 사용하는 플랫폼에서 원치 않는 파일 액세스 문제를 피하기 위해 각 블록에 이것을 추가하고 있습니다 . cmd의 일부로 파일을 설정할 find때 이러한 파일 권한 문제가 발생할 것이라고 예상하지 않는다면 취소할 수 있습니다.
  • 또한 위에서 언급한 계산에만 관심이 있다는 가정에 맞게 $PWD(현재 작업 디렉터리, 로 표시됨 ) 에 대한 모든 출력을 억제했습니다..정기적인현재 하위 디렉터리의 파일입니다.
  • 처음부터 시작하여 전체 하위 디렉터리 트리의 일반 파일 수를 계산하려면 위의 첫 번째 블록에서 전역 옵션을 $PWD생략하면 됩니다 (두 번째 블록에는 그대로 유지).-maxdepth 1find

종속 솔루션 parallel(아래)과의 유사성을 더 잘 강조하기 위해 위의 내용을 다시 작성할 수 있습니다.

$ xargs -0 -I {} sh -c 'printf "%20s:  %d\n" "{}" "$(find "{}" -maxdepth 1 -type f 2>/dev/null| wc -l)"' \
  < <(find . -maxdepth 1 -type d ! -name "." -print0 2>/dev/null)

위에 표시된 대로 parallel치환 에 의존하여 xargs아래와 같이 일부 따옴표를 이스케이프해야 합니다(출력은 이전과 정확히 동일함).

$ parallel -0 -I {} \
  'sh -c "printf \"%20s: %d\n\" \"{}\" \"$(find {} -maxdepth 1 -type f 2>/dev/null | wc -l)\""' \
  :::: < <(find -maxdepth 1 -type d ! -name "." -print0 2>/dev/null)
  • xargsparallel동일한 두 매개변수를 -0 -I {}사용 하여

  • 'sh -c "printf ..."'실행될 쉘 명령은 작은따옴표 사이의 Bourne 쉘입니다.
  • 에 의해 도입된 병렬화에 대한 입력은 "cmd에서 시작하는 첫 번째 하위 수준의 모든 하위 디렉터리 찾기" 출력을 포함하는 ::::프로세스 대체 입력 파일입니다 .<(...)$PWD

답변4

ls 대신 find를 사용하여 솔루션을 찾았습니다.

find * -type d -maxdepth 0 | parallel 'echo -n {}" "; ls {}|wc -l'

관련 정보