찾기로 반환된 파일 이름을 순환하는 방법은 무엇입니까?
x=$(find . -name "*.txt")
echo $x
위의 코드 조각을 Bash 셸에서 실행하면 목록이 아닌 공백으로 구분된 여러 파일 이름을 포함하는 문자열이 표시됩니다.
물론 목록을 얻기 위해 공백으로 더 구분할 수 있지만, 더 나은 방법이 있을 것이라고 확신합니다.
따라서 결과를 반복해서 볼 수 있는 가장 좋은 방법은 무엇입니까?find
명령?
TL;DR: 가장 정확한 답변을 원하신다면 제 개인적인 선호도(이 게시물의 하단 참조)를 원하실 겁니다.
# execute `process` once for each file
find . -name '*.txt' -exec process {} \;
만약 시간이 있다면, 다른 방법들과 그들 대부분의 문제들을 알아보기 위해 나머지를 다 읽으세요.
전체 답변:
가장 좋은 방법은 여러분이 무엇을 하고 싶은지에 따라 다르지만, 여기 몇 가지 옵션이 있습니다.하위 트리의 파일 또는 폴더 이름에 공백이 없는 경우 다음 파일을 반복할 수 있습니다.
for i in $x; do # Not recommended, will break on whitespace
process "$i"
done
더 나은 것은, 변수인 간더나은잘, 변를라니다냅수임시를 .x
:
for i in $(find -name \*.txt); do # Not recommended, will break on whitespace
process "$i"
done
당신이 할 수 있을 때 글을 쓰는 것이 훨씬 좋습니다.현재 디렉터리에 있는 파일에 대한 공백 안전:
for i in *.txt; do # Whitespace-safe but not recursive.
process "$i"
done
을 globstar
디렉터리에 할 수 .
# Make sure globstar is enabled
shopt -s globstar
for i in **/*.txt; do # Whitespace-safe and recursive
process "$i"
done
경우와 파일 이름을 해야 할 .read
:
# IFS= makes sure it doesn't trim leading and trailing whitespace
# -r prevents interpretation of \ escapes.
while IFS= read -r line; do # Whitespace-safe EXCEPT newlines
process "$line"
done < filename
read
와 함께 안전하게 사용할 수 있습니다.find
구분 기호를 적절하게 설정하면 다음과 같습니다.
find . -name '*.txt' -print0 |
while IFS= read -r -d '' line; do
process "$line"
done
더 검색을 좀더복검경다우같수있사다니습용할이음과색의잡한,다▁want▁for있▁to를 사용하는 것이 입니다.find
의 중둘어쪽든이와 함께.-exec
또함는와께와 함께 사용-print0 | xargs -0
:
# execute `process` once for each file
find . -name \*.txt -exec process {} \;
# execute `process` once with all the files as arguments*:
find . -name \*.txt -exec process {} +
# using xargs*
find . -name \*.txt -print0 | xargs -0 process
# using xargs with arguments after each filename (implies one run per filename)
find . -name \*.txt -print0 | xargs -0 -I{} process {} argument
find
에 또사 용하할실을전기에각디파렉일에토수리의다있습니한행명하여령할▁using▁also▁can▁into▁cd▁file▁each수를 사용하여 각에 cd할 수도 있습니다.-execdir
에 -exec
다음을 사용하여 대화형으로 만들 수 있습니다(각 파일에 대해 명령을 실행하기 전에 실행).-ok
에 -exec
(또는)-okdir
에 -execdir
).
엄히말면하다, 둘밀다 둘 다.find
그리고.xargs
(기본적으로) 명령을 실행할 때는 모든 파일을 통과하는 데 필요한 횟수만큼 명령줄에 맞는 인수를 사용합니다.실제로 파일 수가 매우 많지 않은 경우에는 문제가 되지 않으며, 길이를 초과하지만 모두 동일한 명령줄에 필요한 경우에는 SOL이 다른 방법을 찾는 것입니다.
무엇을 하든 루프를 사용하지 마십시오.
# Don't do this
for file in $(find . -name "*.txt")
do
…code using "$file"
done
세 가지 이유:
- 루가균게시작려면하등하프,,
find
완료될 때까지 실행해야 합니다. - 파일 이름에 공백(공백, 탭 또는 새 줄 포함)이 있으면 두 개의 개별 이름으로 처리됩니다.
- 이제는 가능성이 낮지만 명령줄 버퍼를 오버런할 수 있습니다.저장되어 명령버퍼보를 32KB가 저장되어 있다고 가정해 보십시오.
for
은 textloop은 40KB를 합니다.는 마지막바삭다니에서 됩니다.for
루프하면 절대 모를 겁니다.
항상 생성자 사용:
find . -name "*.txt" -print0 | while read -d $'\0' file
do
…code using "$file"
done
가 에는 루프가 됩니다.find
명령이 실행 중입니다.또한 이 명령은 파일 이름에 공백이 있는 경우에도 작동합니다.또한 명령줄 버퍼가 오버플로되지 않습니다.
그-print0
는 새 줄 NULL을 하고, " " " 은 NULL 입니다.-d $'\0'
읽는 동안 NULL을 구분 기호로 사용합니다.
find . -name "*.txt"|while read fname; do
echo "$fname"
done
참고: bmargulies에 표시된 이 방법과 (두 번째) 방법은 파일/폴더 이름에 공백이 있는 상태에서 안전하게 사용할 수 있습니다.
새이 있는 - 인 - 는, "/" " " " " " (" " " " " (" " " " " " " " " " " " " " 을 .-exec
의술의 find
다음과 같이:
find . -name '*.txt' -exec echo "{}" \;
그{}
발견된 항목에 대한 자리 표시자 및\;
를 종료하는 데 사용됩니다.-exec
술어
그리고 완성도를 위해 또 다른 변형을 추가하겠습니다. *nix 방법을 사랑해야 합니다.
find . -name '*.txt' -print0|xargs -0 -n 1 echo
이렇게 하면 인쇄된 항목을 다음과 같이 구분할 수 있습니다.\0
내가 알기로는 파일 또는 폴더 이름의 파일 시스템에서 허용되지 않는 문자이므로 모든 기본을 포함해야 합니다. xargs
그들을 한 명씩 픽업한 다음에...
파일 이름에는 공백과 제어 문자가 포함될 수 있습니다.셸 ( 그 "bash" " "bash" " " (으)로 표시됩니다.x=$(find . -name "*.txt")
이 문제는 전혀 권장되지 않습니다.를 들어 if find find는 find와 같은 파일 이름입니다."the file.txt"
를 위해 두 된 문자열을 얻을 수 있는데,이 처리를 한다면, 은 두 개의 분리된 문자열을 수 .x
고리 모양으로구분 기호(배시)를 변경하여 이를 개선할 수 있습니다.IFS
~ 수) 예: ~\r\n
그러나 파일 이름에는 제어 문자가 포함될 수 있으므로 이 방법은 (완전히) 안전한 방법이 아닙니다.
제 관점에서 파일을 처리하는 데는 두 가지 권장되는(그리고 안전한) 패턴이 패턴은 다음과 같습니다.
루프 및 파일 이름 확장에 사용:
for file in ./*.txt; do
[[ ! -e $file ]] && continue # continue, if file does not exist
# single filename is in $file
echo "$file"
# your code here
done
읽기 중 찾기 & 프로세스 대체 사용
while IFS= read -r -d '' file; do
# single filename is in $file
echo "$file"
# your code here
done < <(find . -name "*.txt" -print0)
언급
패턴 1:
- bash는 일치하는 파일을 찾을 수 없는 경우 검색 패턴("*.txt")을 반환하므로 "파일이 없는 경우 bash" 행이 추가로 필요합니다.Bash 설명서, 파일 이름 확장 참조
- 옵션 셸
nullglob
이 추가 줄을 피하는 데 사용할 수 있습니다. - 에"
failglob
이 없으면 (에서)라는 메시지가 표시됩니다." (Bash Manual에서 - 옵션 셸
globstar
"설정된 경우 파일 이름 확장 컨텍스트에서 사용되는 패턴 '**'은 모든 파일과 0개 이상의 디렉터리 및 하위 디렉터리와 일치합니다.패턴 다음에 '/'이 있으면 디렉토리와 하위 디렉토리만 일치합니다."를 참조하십시오. - 확장을 : 파일이 확을위기옵션타한:
extglob
,nocaseglob
,dotglob
변수 셸수GLOBIGNORE
패턴 2:
은 공백, 바꿈으로 파일을 처리할 수 .
find
와 함께-print0
사용: 파일 이름은 모든 제어 문자로 인쇄되고 NUL로 종료됩니다. Gnu Findutils Manpage, 안전하지 않은 파일 이름 처리, 안전하지 않은 파일 이름 처리, 파일 이름의 비정상적인 문자를 참조하십시오.데이비드 A를 보세요.이 주제에 대한 자세한 내용은 아래 휠러를 참조하십시오.검색 결과를 잠시 동안 루프에서 처리할 수 있는 몇 가지 패턴이 있습니다.다른 사람들(케빈, David W.)은 파이프를 사용하여 이 작업을 수행하는 방법을 보여주었습니다.
files_found=1 find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do # single filename in $file echo "$file" files_found=0 # not working example # your code here done [[ $files_found -eq 0 ]] && echo "files found" || echo "no files found"
코드를 해 보면,이 작동하지 것을 알 수 것입니다: 사보작않것는알을다있수습니는다지이하동면해용를.
files_found
는 항상 "true"이며 코드는 항상 "no found files"로 반향됩니다.이유: 파이프라인의 각 명령은 별도의 하위 셸에서 실행되므로 루프 내부의 변경된 변수(별도의 하위 셸)는 주 셸 스크립트의 변수를 변경하지 않습니다.이러한 이유로 프로세스 대체를 "더 나은", 더 유용하고 더 일반적인 패턴으로 사용할 것을 권장합니다.
파이프라인에 있는 루프에서 변수 설정을 참조하십시오. 왜 그들은 사라지나요...(Greg's Bash FAQ에서) 이 주제에 대한 자세한 내용을 참조하십시오.
추가 참고 자료 및 출처:
(@Socowi의 탁월한 속도 향상 기능을 포함하도록 업데이트됨)
아무 것이나$SHELL
find . -name "*.txt" -exec $SHELL -c '
for i in "$@" ; do
echo "$i"
done
' {} +
다 했어요.
원래 답변(짧지만 더 느림):
find . -name "*.txt" -exec $SHELL -c '
echo "$0"
' {} \;
파일 이름에 새 줄이 포함되어 있지 않다고 가정할 수 있는 경우 다음 출력을 읽을 수 있습니다.find
다음 명령을 사용하여 Bash 어레이로 이동합니다.
readarray -t x < <(find . -name '*.txt')
참고:
-t
인들원readarray
새 라인을 벗깁니다.- 그러면 안 돼요.
readarray
파이프 안에 있으므로 프로세스가 대체됩니다. readarray
Bash 4부터 사용할 수 있습니다.
Bash4은 Bash 4.4를 합니다.-d
구분 기호를 지정하기 위한 매개 변수입니다.newline이 할 수 .: " " " null null null " " " 파 " 이 " 구 " 면 " 하 " 에 " 이 " 된 " 줄 " 줄 " 포 " 함 " 대 " 도 " 니 " 다 " 합 " 동 " 에 " 작 " 새 " 우 " 경 " 문 " 이 " 드 " 신 " 새 " 름 " 문 " 파 " 분 " 일 " 을
readarray -d '' x < <(find . -name '*.txt' -print0)
readarray
다음과 같이 호출할 수도 있습니다.mapfile
동일한 옵션을 사용합니다.
참조: https://mywiki.wooledge.org/BashFAQ/005#Loading_lines_from_a_file_or_stream
# Doesn't handle whitespace
for x in `find . -name "*.txt" -print`; do
process_one $x
done
or
# Handles whitespace and newlines
find . -name "*.txt" -print0 | xargs -0 -n 1 process_one
이 하는 것이라고 합니다. ( 뒤에 명령어를 입력합니다.)while done
):
while read fname; do
echo "$fname"
done <<< "$(find . -name "*.txt")"
이 대답보다 더 나은 이유는while
루프는 여기에 따라 하위 셸에서 실행됩니다. 만약 당신이 이 답변을 사용한다면, 변수 변화는 다음 이후에 볼 수 없습니다.while
루프 내부의 변수를 수정하려는 경우 루프를 선택합니다.
변수에 처음 할당되고 IFS가 새 라인으로 전환된 find를 다음과 같이 사용합니다.
FilesFound=$(find . -name "*.txt")
IFSbkp="$IFS"
IFS=$'\n'
counter=1;
for file in $FilesFound; do
echo "${counter}: ${file}"
let counter++;
done
IFS="$IFSbkp"
@Konrad Rudolph가 언급했듯이, 이것은 파일 이름의 "새 줄"에서는 작동하지 않습니다.명령어 출력을 루프오버해야 하는 경우를 대부분 커버하기 때문에 여전히 편리하다고 생각합니다.
Kevin이 이미 상위 답변에 게시한 것처럼 가장 좋은 해결책은 bashglob과 함께 for 루프를 사용하는 것이지만 bashglob은 기본적으로 재귀적이지 않기 때문에 bash 재귀 함수를 통해 이 문제를 해결할 수 있습니다.
#!/bin/bash
set -x
set -eu -o pipefail
all_files=();
function get_all_the_files()
{
directory="$1";
for item in "$directory"/* "$directory"/.[^.]*;
do
if [[ -d "$item" ]];
then
get_all_the_files "$item";
else
all_files+=("$item");
fi;
done;
}
get_all_the_files "/tmp";
for file_path in "${all_files[@]}"
do
printf 'My file is "%s"\n' "$file_path";
done;
관련 질문:
- 숨겨진 파일을 포함한 디렉터리를 통해 Bash 루프 실행
- Bash의 지정된 디렉토리에서 파일을 재귀적으로 나열
- ls 명령: 파일당 한 줄씩 재귀적인 전체 경로 목록을 얻으려면 어떻게 해야 합니까?
- 현재 디렉터리를 기준으로 하는 경로를 사용하여 Linux CLI에서 재귀적으로 파일 나열
- 모든 디렉터리 및 파일을 재귀적으로 나열
- bash 스크립트, 디렉토리에 있는 모든 파일의 배열 생성
- 폴더에 있는 모든 파일의 이름을 포함하는 배열을 만들려면 어떻게 해야 합니까?
- 폴더에 있는 모든 파일의 이름을 포함하는 배열을 만들려면 어떻게 해야 합니까?
- 셸 스크립트의 디렉터리에 있는 파일 목록을 가져오는 방법은 무엇입니까?
#3phk를 하여 @phk의 #3:
을 계속 할 수 있음 (stdin을 사용함)
while IFS= read -r f <&3; do
echo "$f"
done 3< <(find . -iname "*filename*")
된 파일 이름은 ▁the▁by▁▁put▁you로 입력할 수 .find
다음과 같은 배열로:
array=()
while IFS= read -r -d ''; do
array+=("$REPLY")
done < <(find . -name '*.txt' -print0)
이제 어레이를 순환하여 개별 항목에 액세스하고 원하는 작업을 수행할 수 있습니다.
참고: 화이트 스페이스 세이프입니다.
은 당신의 를 저장할 수 .find
출력을 나중에 다음과 같이 사용하려면 배열로 출력합니다.
array=($(find . -name "*.txt"))
이제 각 요소를 새 줄로 인쇄하려면 다음 중 하나를 사용할 수 있습니다.for
배열의 모든 요소에 대한 루프 반복 또는 printf 문을 사용할 수 있습니다.
for i in ${array[@]};do echo $i; done
또는
printf '%s\n' "${array[@]}"
다음을 사용할 수도 있습니다.
for file in "`find . -name "*.txt"`"; do echo "$file"; done
각 파일 이름을 새 줄로 인쇄합니다.
만 find
다음 중 하나를 사용할 수 있습니다.
find . -name "*.txt" -print 2>/dev/null
또는
find . -name "*.txt" -print | grep -v 'Permission denied'
그러면 오류 메시지가 제거되고 파일 이름이 새 줄의 출력으로만 표시됩니다.
당신이 로 저장하는 그렇지 않으면 그 ▁from▁output▁print▁the▁and▁if다▁directly▁you▁canen좋▁it니파,▁with의 출력을 직접 인쇄할 수 있습니다. 그렇지 않으면 해당 공간을 사용할 필요가 없으며 출력을 직접 인쇄할 수 있습니다.find
.
function loop_through(){
length_="$(find . -name '*.txt' | wc -l)"
length_="${length_#"${length_%%[![:space:]]*}"}"
length_="${length_%"${length_##*[![:space:]]}"}"
for i in {1..$length_}
do
x=$(find . -name '*.txt' | sort | head -$i | tail -1)
echo $x
done
}
루프를 위한 파일 목록의 길이를 파악하기 위해 첫 번째 명령 "wc -l"을 사용했습니다.
이 명령은 변수로 설정됩니다.
그런 다음 for 루프가 읽을 수 있도록 변수에서 뒤에 오는 공백을 제거해야 합니다.
find <path> -xdev -type f -name *.txt -exec ls -l {} \;
그러면 파일이 나열되고 속성에 대한 세부 정보가 제공됩니다.
또 다른 대안은 bash를 사용하지 않고 Python을 호출하여 무거운 리프팅을 하는 것입니다.저의 다른 답변인 bash 솔루션이 너무 느려서 이 문제에 다시 답했습니다.
이 솔루션을 사용하여 인라인 Python 스크립트에서 bash 파일 어레이를 구축합니다.
#!/bin/bash
set -eu -o pipefail
dsep=":" # directory_separator
base_directory=/tmp
all_files=()
all_files_string="$(python3 -c '#!/usr/bin/env python3
import os
import sys
dsep="'"$dsep"'"
base_directory="'"$base_directory"'"
def log(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def check_invalid_characther(file_path):
for thing in ("\\", "\n"):
if thing in file_path:
raise RuntimeError(f"It is not allowed {thing} on \"{file_path}\"!")
def absolute_path_to_relative(base_directory, file_path):
relative_path = os.path.commonprefix( [ base_directory, file_path ] )
relative_path = os.path.normpath( file_path.replace( relative_path, "" ) )
# if you use Windows Python, it accepts / instead of \\
# if you have \ on your files names, rename them or comment this
relative_path = relative_path.replace("\\", "/")
if relative_path.startswith( "/" ):
relative_path = relative_path[1:]
return relative_path
for directory, directories, files in os.walk(base_directory):
for file in files:
local_file_path = os.path.join(directory, file)
local_file_name = absolute_path_to_relative(base_directory, local_file_path)
log(f"local_file_name {local_file_name}.")
check_invalid_characther(local_file_name)
print(f"{base_directory}{dsep}{local_file_name}")
' | dos2unix)";
if [[ -n "$all_files_string" ]];
then
readarray -t temp <<< "$all_files_string";
all_files+=("${temp[@]}");
fi;
for item in "${all_files[@]}";
do
OLD_IFS="$IFS"; IFS="$dsep";
read -r base_directory local_file_name <<< "$item"; IFS="$OLD_IFS";
printf 'item "%s", base_directory "%s", local_file_name "%s".\n' \
"$item" \
"$base_directory" \
"$local_file_name";
done;
관련:
찾는 대신 grep를 사용하면 어떨까요?
ls | grep .txt$ > out.txt
이제 이 파일을 읽을 수 있으며 파일 이름은 목록 형식입니다.
언급URL : https://stackoverflow.com/questions/9612090/how-to-loop-through-file-names-returned-by-find
'programing' 카테고리의 다른 글
Windows 8.1의 고해상도 화면에서 매우 작은 Eclipse 인터페이스 아이콘 (0) | 2023.05.26 |
---|---|
.NET Core, .NET Framework 및 Xamarin의 차이점은 무엇입니까? (0) | 2023.05.26 |
지정된 커밋의 커밋 메시지 인쇄 (0) | 2023.05.26 |
Xcode에서 활성 스킴의 이름을 변경하는 방법은 무엇입니까? (0) | 2023.05.26 |
부모와 약속을 나누는 방법 (0) | 2023.05.26 |