답변:
불필요하게 ls출력을 변수에 넣은 다음 echo모든 색상을 제거하는 ing 대신에
ls -a1
에서 man ls
-1 list one file per line. Avoid '\n' with -q or -b
나는 출력을 ls제외하고 는 아무것도 표시 하지 않는 것이 좋습니다 :)
예를 들어, 쉘 글로브 및 for루프를 사용하여 파일로 무언가를 수행하십시오.
shopt -s dotglob # to include hidden files*
for i in R/*; do echo "$i"; done
*이 현재 디렉토리에 포함되지 않습니다 .또는 부모 ..생각을
echo "$i"with 를 대체하는 것이 좋습니다 printf "%s\n" "$i" . 파일 이름이 "-"로 시작하면 질식하지 않습니다. 실제로 대부분의 시간 echo something은로 대체해야합니다 printf "%s\n" "something".
R여기에서 시작 하지만 일반적으로 그렇습니다. 그러나 스크립팅의 경우에도 항상 선행 -경로 를 수용 하려고 하면 문제가 발생합니다. 사람들은 --일부 명령 만 지원하는 옵션은 옵션의 끝을 나타냅니다. 지원하지 않는 find . [tests] -exec [cmd] -- {} \;곳을 보았습니다 . 어쨌든 모든 길은 시작됩니다 ! 하지만 그 교체에 대한 인수를 없습니다 와 함께 . 여기서 전체 루프를로 바꿀 수 있습니다 . Bash는 빌트인으로 제공되므로 인수의 수 / 길이에 효과적으로 제한이 없습니다. [cmd]--.echoprintf '%s\n'printf '%s\n' R/*printf
@muru가 제안한 대로 인용 부호로 묶으면 실제로 요청한 내용을 수행하지만 배열을 사용하는 것도 고려할 수 있습니다. 예를 들면 다음과 같습니다.
IFS=$'\n' dirs=( $(find . -type d) )
The IFS=$'\n'bash는 줄 바꿈 문자로만 출력을 분할하도록 bash에 지시합니다. o 배열의 각 요소를 가져옵니다. 그렇지 않으면 공백으로 분할되므로 a file name with spaces.txt하나가 아닌 5 개의 개별 요소가됩니다. 파일 / 디렉토리 이름에 개행 ( \n)이 포함될 수 있으면이 방법이 중단됩니다 . 명령 출력의 각 줄 을 배열의 요소로 저장합니다.
이전 스타일 `command`을 $(command)선호하는 구문으로 변경했습니다.
이제 $dirs각 bof의 요소가 이전 명령의 출력 행인 이라는 배열 이 있습니다. 예를 들면 다음과 같습니다.
$ find . -type d
.
./olad
./ho
./ha
./ads
./bar
./ga
./da
$ IFS=$'\n' dirs=( $(find . -type d) )
$ for d in "${dirs[@]}"; do
echo "DIR: $d"
done
DIR: .
DIR: ./olad
DIR: ./ho
DIR: ./ha
DIR: ./ads
DIR: ./bar
DIR: ./ga
DIR: ./da
이제 bash 이상한 점 ( 여기 참조 ) IFS때문에이 작업을 수행 한 후 원래 값으로 다시 설정해야합니다 . 따라서 저장하고 다시 서명하십시오.
oldIFS="$IFS"
IFS=$'\n' dirs=( $(find . -type d) )
IFS="$oldIFS"
또는 수동으로 수행하십시오.
IFS=" "$'\t\n '
또는 현재 터미널을 닫으십시오. 새 IFS는 원래 IFS를 다시 설정합니다.
duOP가 보이도록 만드는 부분은 출력 형식을 보존하는 데 더 관심이 있습니다.
mapfile쉽게 만든다.당신이 경우 우선 고려 필요가 전혀 명령의 출력을 저장합니다. 그렇지 않으면 명령을 실행하십시오.
명령의 출력을 행의 배열로 읽으려면 글 로빙을 비활성화 IFS하고 행을 분할하도록 설정 하고 ( )배열 생성 구문 내에서 명령 대체를 사용 하고 IFS나중에 재설정하는 것이 한 가지 방법입니다 . 이는 더 복잡 보인다보다. terdon의 답변 은 그러한 접근법 중 일부를 다룹니다. 그러나 대신 텍스트를 행 배열로 읽는 mapfileBash 쉘의 기본 제공 명령 인을 사용하는 것이 좋습니다 . 다음은 파일에서 읽는 간단한 사례입니다.
mapfile < filename
이것은이라는 배열로 행을 읽습니다 MAPFILE. 입력 리디렉션이 없으면로 명명 된 파일 대신 쉘의 표준 입력 (일반적으로 터미널)을 읽 습니다 . default 이외의 배열로 읽으려면 해당 이름을 전달하십시오. 예를 들어, 이것은 배열을 읽습니다 .< filenamefilenameMAPFILElines
mapfile lines < filename
변경할 다른 기본 동작은 줄 끝의 줄 바꿈 문자 가 그대로 남아 있다는 것입니다. 그것들은 각 배열 요소의 마지막 문자로 나타납니다 (입력이 개행 문자없이 끝나지 않는 한, 마지막 요소에는 문자가 없습니다). 이 개행을 정리하여 배열에 나타나지 않도록하려면 -t옵션을로 전달하십시오 mapfile. 예를 들어, 이것은 배열을 읽고 records배열 요소에 후행 줄 바꿈 문자를 쓰지 않습니다.
mapfile -t records < filename
-t배열 이름을 전달하지 않고도 사용할 수 있습니다 . 즉, 암시 적 배열 이름으로 MAPFILE도 작동합니다.
mapfile지원 내장 쉘은 다른 옵션과는 다른 방법으로 호출 할 수 있습니다 readarray. 자세한 내용은 help mapfile(및 help readarray)를 실행 하십시오.
그러나 파일에서 읽기를 원하지 않고 명령의 출력에서 읽기를 원합니다. 이를 위해서는 프로세스 대체를 사용하십시오 . 이 명령은 명령 줄 읽어 some-command와 arguments...의 명령 행 인수와 그들의 장소로 mapfile의 기본 배열 MAPFILE을 제거 자신의 종료 개행 문자를 :
mapfile -t < <(some-command arguments...)
프로세스 대체는 실행 결과를 읽을 수 있는 실제 파일 이름으로 대체 됩니다 . 파일은 일반 파일이 아닌 명명 된 파이프 이며 우분투에서는 이름이 (때로는 다른 숫자가 아닌 )로 지정되지만 셸이 처리하기 때문에 세부 정보에 신경 쓸 필요가 없습니다. 무대 뒤에서<(some-command arguments...)some-command arguments.../dev/fd/6363
대신 대신 사용하여 프로세스 대체를 잊을 수 있다고 생각할 수도 있지만 여러 명령 의 파이프 라인이 으로 구분 되면 Bash는 하위 명령으로 모든 명령을 실행 하기 때문에 작동하지 않습니다 . 따라서 둘 과 자신의 실행 환경 에서 초기화,하지만 당신은 파이프 라인을 실행하는 쉘의 환경에서 분리합니다. 실행 되는 서브 쉘에서 배열 이 채워지지만 명령이 종료되면 해당 배열이 삭제됩니다. 발신자를 위해 생성되거나 수정되지 않습니다.some-command arguments... | mapfile -t|some-command arguments...mapfile -tmapfile -tMAPFILEMAPFILE
다음을 사용하는 경우 terdon의 답변 예제 가 다음과 같습니다 mapfile.
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
그게 다야. 설정 여부IFS 를 확인할 필요가 없으며 설정 여부와 값을 추적하여 줄 바꿈으로 설정 한 다음 나중에 재설정하거나 재설정 해제 할 필요가 없습니다. 파일 이름 에 , 및가 포함 된 후 나중에 다시 활성화 할 수 있으므로 해당 방법을 심각하게 사용하는 경우 실제로 필요한 글 로빙 (예 :) 을 비활성화 할 필요가 없습니다 .set -f*?[set +f
또한 특정 루프를 단일 printf명령으로 완전히 대체 할 수도 있습니다. 하지만 실제로 사용하는 방법은 아니지만 다른 방법을 mapfile사용 mapfile하여 배열을 채울 수 있습니다. 더 짧은 버전은 다음과 같습니다.
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
그것은 같은 것을 명심하는 것이 중요 terdon 언급 , 운영 줄 단위는 항상 적절하지 않고, 줄 바꿈을 포함하는 파일 이름에서 제대로 작동하지 않습니다. 그런 식으로 파일 이름을 지정하지 않는 것이 좋지만 우연히 포함하여 발생할 수 있습니다 .
"모든 시나리오에 대한 일반적인 명령"과 mapfile그 목표 를 어느 정도 사용하는 방법 을 요청했지만 요구 사항을 다시 고려해야합니다.
위에 표시된 작업은 단일 find명령으로 더 잘 수행됩니다 .
find . -type d -printf 'DIR: %p\n'
각 줄의 시작 부분에 sed추가 DIR: 하는 것과 같은 외부 명령을 사용할 수도 있습니다 . 이것은 다소 추악한 일이며 find 명령과 달리 줄 바꿈이 포함 된 파일 이름 내에 추가 "접두사"를 추가하지만 입력에 관계없이 작동하므로 요구 사항을 충족 하므로 출력을 변수 또는 배열 :
find . -type d | sed 's/^/DIR: /'
some-command디렉토리 경로를 인수 로 실행 및 전달하는 등 발견 된 각 디렉토리에 대한 조치를 나열하고 수행해야하는 경우에도이를 수행 find할 수 있습니다.
find . -type d -print -exec some-command {} \;
다른 예로, 각 줄에 접두사를 추가하는 일반적인 작업으로 돌아가 봅시다. help mapfile라인 의 출력을보고 싶지만이라고 가정 해보십시오 . 실제로 mapfile이것을 사용하지 않을 수도 있고 쉘 변수 또는 쉘 배열로 읽는 다른 방법도 사용하지 않을 것 입니다. 치죠이 help mapfile | cat -n의 I 원하는 서식을 제공하지 않습니다, 내가 사용할 수 있습니다 awk:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
명령의 모든 출력을 단일 변수 또는 배열로 읽는 것이 유용하고 적절한 경우가 있지만 주요 단점이 있습니다. 기존 명령 또는 기존 명령의 조합이 이미 필요한 것보다 나은 작업을 수행 할 수있을 때 쉘 사용의 추가 복잡성을 처리해야 할뿐만 아니라 명령의 전체 출력을 메모리에 저장해야합니다. 때로는 이것이 문제가 아니라는 것을 알지만 때로는 큰 파일을 처리하고있을 수 있습니다.
일반적으로 시도되고 때로는 올바르게 수행되는 대안 read -r은 루프에서 입력을 한 줄씩 읽는 것 입니다. 이후 라인에서 작업하는 동안 이전 라인을 저장할 필요가없고 긴 입력을 사용해야하는 경우보다 낫습니다 mapfile. 그러나 작업을 수행 할 수있는 명령으로 파이프 할 수있는 경우에는 피해야 합니다. 대부분의 경우 입니다.