답변:
bash의 강력한 방법 중 하나는 배열로 확장하고 첫 번째 요소 만 출력하는 것입니다.
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(넌 심지어 단지 수 echo $files
, 누락 된 인덱스는 [0]로 처리된다.)
파일 이름을 확장 할 때 공간 / 탭 / 줄 바꾸기 및 기타 메타 문자를 안전하게 처리합니다. 사실상 로케일 설정은 "첫 번째"를 변경할 수 있습니다.
bash 완성 함수를 사용하여 대화식 으로이 작업을 수행 할 수도 있습니다 .
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]} # string to expand
if compgen -G "$cur*" > /dev/null; then
local files=( ${cur:+$cur*} ) # don't expand empty input as *
[ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
fi
}
complete -o bashdefault -F _echo echo
이것은 _echo
함수를 바인드하여 echo
명령에 인수를 완료합니다 (정상 완료를 재정의 함). 위의 코드에 추가 "*"가 추가되어 부분 파일 이름에서 탭을 누르면 올바른 일이 발생할 수 있습니다.
코드는 설정되거나 가정되는 것이 아니라 약간 복잡합니다. nullglob
( shopt -s nullglob
) compgen -G
glob를 일부 일치 항목으로 확장 한 다음 안전하게 배열로 확장하고 마지막으로 인용을 강력하게 설정하도록 COMPREPLY를 설정합니다.
bash 's로 부분적 으로이 작업을 수행 할 수 있지만 (프로그래밍 방식으로 glob 확장) compgen -G
stdout에 인용되지 않은 출력으로 강력하지 않습니다.
평소와 같이 완성은 다소 어려워 환경 변수를 포함한 다른 것들의 완성을 깨뜨립니다 ( 기본 동작 에뮬레이션에 대한 자세한 내용은 여기_bash_def_completion()
기능 참조 ).
compgen
완성 함수 외부 에서도 사용할 수 있습니다 .
files=( $(compgen -W "$pattern") )
한 가지 주목할 점은 "~"는 구형이 아니며 $ variables 및 기타 확장과 마찬가지로 별도의 확장 단계에서 bash에 의해 처리됩니다. compgen -G
파일 이름을 compgen -W
붙일 뿐이지 만 bash의 기본 확장을 모두 제공 하지만 확장이 너무 많을 수도 있습니다 ( ``
및 포함 $()
). 달리 -G
의이 -W
되어 안전하게 인용 (나는 차이를 설명 할 수 없다). -W
토큰을 확장하는 것이 목적이므로, 이러한 파일이 존재하지 않더라도 "a"를 "a"로 확장하므로 이상적이지 않을 수 있습니다.
이것은 이해하기 쉽지만 원하지 않는 부작용이있을 수 있습니다.
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
그때:
touch $'curious \n filename'
echo curious*
tab
사용주의 printf %q
안전하게 값을 인용 할 수 있습니다.
마지막 옵션 중 하나는 GNU 유틸리티에서 0으로 구분 된 출력을 사용하는 것입니다 ( bash FAQ 참조 ).
pattern="*.txt"
while IFS= read -r -d $'\0' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )
이 옵션을 사용하면 정렬 순서를 조금 더 제어 할 수 있습니다 (글로브를 확장 할 때의 순서는 로케일에 따라 다르고 LC_COLLATE
케이스가 접히지 않을 수도 있습니다). 그렇지 않으면 작은 문제에 대해 다소 큰 망치입니다.
zsh에서는 [1]
glob 한정자를 사용하십시오 . 이 특수 사례는 최대 하나의 일치 항목을 반환하더라도 여전히 목록이며 할당 (배열 할당은 제외)과 같은 단일 단어를 예상하는 컨텍스트에서는 글로브가 확장되지 않습니다.
echo *.txt([1])
ksh 또는 bash에서는 전체 일치 목록을 배열로 채우고 첫 번째 요소를 사용할 수 있습니다.
tmp=(*.txt)
echo "${tmp[0]}"
모든 쉘에서 위치 매개 변수를 설정하고 첫 번째 매개 변수를 사용할 수 있습니다.
set -- *.txt
echo "$1"
이 위치 매개 변수를 방해합니다. 원하지 않으면 서브 쉘을 사용할 수 있습니다.
echo "$(set -- *.txt; echo "$1")"
자체 위치 매개 변수 세트가있는 함수를 사용할 수도 있습니다.
set_to_first () {
eval "$1=\"\$2\""
}
set_to_first f *.txt
echo "$f"
*.txt([1,n])
간단한 해결책 :
sh -c 'echo "$1"' sh *.txt
또는 printf
원하는 경우 사용 하십시오.
나는 똑같은 것을 궁금해 하면서이 오래된 질문을 우연히 발견했습니다. 나는 이것으로 끝났다.
echo $(ls *.txt | head -n1)
당신은 물론, 대체 할 수 head
로 tail
및 -n1
다른 번호.
이름에 줄 바꿈이있는 파일로 작업하는 경우 위의 내용이 작동하지 않습니다. 줄 바꿈으로 작업하려면 다음 중 하나를 사용할 수 있습니다.
ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g'
(BSD에서는 작동하지 않습니다)ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
echo -e "$(ls -b *.txt | head -n1)"
(특수 문자로 작동)내가 자주 접하는 유스 케이스는 glob 확장 후 top / bottom 디렉토리를 정의하는 것입니다 (예 : 버전이있는 SDK 또는 빌드 도구로 가득 찬 디렉토리). 이 상황에서는 일반적으로 쉘 스크립트 내의 몇몇 위치에서 사용하기 위해 해당 디렉토리 이름을 변수에 저장하려고합니다.
이 명령은 일반적으로 나를 위해 수행합니다.
export SDK_DIR=$(dirname /path/to/versioned/sdks/*/. | tail -n1)
면책 조항 : Glob 확장은 폴더를 semver별로 정렬하지 않습니다. 당신은 경고를 받았습니다. 당신이있는 경우에 이것은 대단한 Dockerfile
디렉토리의 한 버전을하지만, 그 디렉토리 버전은 이미지에 이미지 다를 수 🤷
mkdir "$(echo one; echo two)"
의미하는 바를 보십시오 .
tail
?
dirname
은 단일 경로 이름 만 사용하므로 특정 구현에서 지원하지 않는 한 여러 경로 이름에 대한 작업에 의존 할 수 없습니다.