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 -0ne
chmod +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'
"내가 할 수 있을까..."와 같은 좋은 대답인지는 잘 모르겠습니다.
파일 및 디렉터리 목록을 생성합니다.
적절한 접미사를 사용하여 인쇄하십시오.
그런 다음 항목이 올바른 컨텍스트에 표시되도록 전체 결과가 정렬됩니다.
이름에 줄바꿈이 포함된 항목을 처리하려면 약간의 추가 작업이 필요합니다.