명령 트리처럼 디렉터리의 모든 파일을 반복하고 모든 파일 이름을 인쇄하는 방법

명령 트리처럼 디렉터리의 모든 파일을 반복하고 모든 파일 이름을 인쇄하는 방법

tree명령을 모방하고 디렉터리의 모든 파일과 하위 디렉터리를 반복하고 모든 파일 이름을 에코하려면 어떻게 해야 합니까 ?

나는 디렉토리 내의 하위 디렉토리가 여전히 디렉토리로 간주된다고 생각하여 다음과 같이 했습니다.

for FILE in *; do
if [ -d $FILE ]
then echo "$FILE is a directory";
else echo "$FILE is a file"
fi
done

하위 디렉토리 내에서 재귀 및 루프를 만들고 모든 파일을 반복하고 파일 이름 + 경로를 인쇄하려면 어떻게 해야 합니까?

bash를 사용하는 쉘 스크립트입니다. 감사해요!

답변1

다음은 명령으로 구현된 terdon 쉘 함수와 동일합니다 find.

find . -exec sh -c 'for f; do
                      [ -d "$f" ] && is_dir="" || is_dir="not "
                      printf "\"%s\" is %sa directory\n" "$f" "$is_dir"
                    done' sh {} +

또는 find & perl을 사용하십시오(각 디렉토리 수준을 공백 두 개로 들여쓰기하고 파일 이름 구분자로 NUL을 사용하십시오).

find . -print0 | \
  perl -0ne '$is_dir = -d $_ ? "" : "not ";
             $f = $_; $indent = ($f =~ s=/==g);
             printf "  " x $indent . "%s is %sa directory\n", $_, $is_dir'

대부분의 Perl 한 줄짜리 코드는 간단하고 명확하지만 설명이 필요할 수도 있습니다.

$f = $_; $indent = ($f =~ s=/==g);

이는 $_현재 입력 레코드(즉, 파일 이름)를 변수에 복사합니다 $f. 그런 다음 /변경 횟수를 변수에 저장하는 동안 $f를 수정하여 모든 문자를 제거합니다 $indent. $f더 이상 사용되지 않으며 변경을 방지하기 위한 일회성 변수일 뿐입니다 $_.

이 변수는 printf 형식 문자열을 구성하기 위해 $indent반복 연산자( x곱셈과 유사하지만 문자열의 경우 참조 )와 함께 사용됩니다.man perlop


파일 및 디렉터리 수를 계산하려면 한 줄에 3자리 넓은 숫자를 사용하세요.

$ find . -print0 | perl -0ne '
    if ( -d $_ ) {
      $is_dir = "";
      $dirs++
    } else {
      $is_dir = "not ";
      $files++
    };

    $f = $_; $indent = ($f =~ s=/==g);
    #printf "  " x $indent . "%s is %sa directory\n", $_, $is_dir;
    printf "%3i: " . "  " x $indent . "%s is %sa directory\n", $., $_, $is_dir;

    END {
      # simple method to determine singular or plural word forms
      # without using the Lingua::EN::Inflect module
      # (see https://metacpan.org/release/Lingua-EN-Inflect)
      my $d_word = ($dirs  != 1) ? "directories" : "directory";
      my $f_word = ($files != 1) ? "files" : "file";

      print "\n$dirs $d_word, $files $f_word\n";
    }'

이 줄의 Perl 부분은 명령줄에서 편집하는 것이 더 이상 합리적이지 않은 지점에 도달했습니다. 터무니없이 긴 줄을 처리하는 것은 완전한 PITA이기 때문입니다. 텍스트 편집기를 사용하는 것이 훨씬 쉽습니다. $PATH 어딘가에 스크립트 파일(즉, ~/bin/존재하지 않는 경우 PATH에 추가)에 첫 번째 줄로 저장해야 합니다./usr/local/bin#!/usr/bin/perl -0nechmod +x

답변2

디렉터리를 다른 모든 것과 별도로 유지하고 싶다면 재귀 함수를 사용하여 다음과 같이 할 수 있습니다.

#!/bin/bash

## Do not expand globs to themselves if they don't match
shopt -s nullglob

list_files(){
  ## if a target has been passed as an argument, use that; if not,
  ## default to '.', the current directory.
  target=${1:-.}
  for i in "$target"/*; do
    if [ -d "$i" ]; then
      list_files "$i"
    else
      echo "$i is not a directory"
    fi
  done
}

list_files "$@"

다음과 같은 디렉토리 구조가 주어지면:

$ tree
.
├── 1.txt
├── 2.txt
├── 3.txt
├── dir1
│   ├── subdir1
│   │   ├── file
│   │   ├── subsubdir1
│   │   └── subsubdir2
│   └── subdir2
│       ├── file
│       ├── subsubdir1
│       └── subsubdir2
├── dir2
│   ├── subdir1
│   │   ├── subsubdir1
│   │   └── subsubdir2
│   └── subdir2
│       ├── subsubdir1
│       └── subsubdir2
└── symlink -> 2.txt

15 directories, 6 files

위의 코드는 다음을 생성합니다.

$ bar.sh
"./1.txt" is not a directory
"./2.txt" is not a directory
"./3.txt" is not a directory
"./dir1" is a directory
"./dir1/subdir1" is a directory
"./dir1/subdir1/file" is not a directory
"./dir1/subdir1/subsubdir1" is a directory
"./dir1/subdir1/subsubdir2" is a directory
"./dir1/subdir2" is a directory
"./dir1/subdir2/file" is not a directory
"./dir1/subdir2/subsubdir1" is a directory
"./dir1/subdir2/subsubdir2" is a directory
"./dir2" is a directory
"./dir2/subdir1" is a directory
"./dir2/subdir1/subsubdir1" is a directory
"./dir2/subdir1/subsubdir2" is a directory
"./dir2/subdir2" is a directory
"./dir2/subdir2/subsubdir1" is a directory
"./dir2/subdir2/subsubdir2" is a directory
"./symlink" is not a directory

또한 디렉터리 트리의 깊이를 나타내는 오프셋을 추가하고 모든 디렉터리를 먼저 처리하여 더 정렬된 출력을 얻으면 읽기 쉽게 만들 수 있습니다.

#!/bin/bash

## Do not expand globs to themselves if they don't match
shopt -s nullglob

list_files(){
  ## if a target has been passed as an argument, use that; if not,
  ## default to '.', the current directory.
  target=${1:-.}

  for i in "$target"/*; do
    offset=${2:-""}
    if [ -d "$i" ]; then
      printf '%s"%s" is a directory\n' "$offset" "$i"
      for subdir in "$i"/*/; do
        new_offset="$offset   "
        list_files "$i" "$new_offset"
        new_offset=""
      done
    else
      printf '%s"%s" is not a directory\n' "$offset" "$i" 
    fi
  done
}

list_files "$@"

그러면 위의 입력 예에 대해 다음과 같은 출력이 생성됩니다.

$ foo.sh 
"./1.txt" is not a directory
"./2.txt" is not a directory
"./3.txt" is not a directory
"./dir1" is a directory
   "./dir1/subdir1" is a directory
      "./dir1/subdir1/file" is not a directory
      "./dir1/subdir1/subsubdir1" is a directory
      "./dir1/subdir1/subsubdir2" is a directory
         "./dir1/subdir1/subsubdir2/file" is not a directory
   "./dir1/subdir2" is a directory
      "./dir1/subdir2/file" is not a directory
      "./dir1/subdir2/subsubdir1" is a directory
      "./dir1/subdir2/subsubdir2" is a directory
"./dir2" is a directory
   "./dir2/subdir1" is a directory
      "./dir2/subdir1/subsubdir1" is a directory
      "./dir2/subdir1/subsubdir2" is a directory
   "./dir2/subdir2" is a directory
      "./dir2/subdir2/subsubdir1" is a directory
      "./dir2/subdir2/subsubdir2" is a directory
"./symlink" is not a directory

이 방법은 심볼릭 링크 디렉터리로 이동됩니다. 예를 들어, 다음을 추가하면:

$ ln -s dir2 dirsym
$ ls -ld dirsym
lrwxrwxrwx 1 terdon terdon 4 Apr  8 16:03 dirsym -> dir2

그런 다음 dirsym디렉터리로 처리되고 해당 내용이 대상과 동일하게 보고되어 출력이 dir2효과적으로 반복됩니다 . dir2이것이 바람직한 행동인지는 모르겠습니다. 그렇지 않은 경우Katz의 find접근 방식은 반대입니다..

답변3

(
  find . -type f -print0 | xargs -0 printf '%s is a file\0'
  find . -type d -print0 | xargs -0 printf '%s is a directory\0'
) | sort -z | tr '\0' '\n'

"내가 할 수 있을까..."와 같은 좋은 대답인지는 잘 모르겠습니다.

파일 및 디렉터리 목록을 생성합니다.

적절한 접미사를 사용하여 인쇄하십시오.

그런 다음 항목이 올바른 컨텍스트에 표시되도록 전체 결과가 정렬됩니다.

이름에 줄바꿈이 포함된 항목을 처리하려면 약간의 추가 작업이 필요합니다.

관련 정보