저는 bash 프롬프트 PS1에서 작업 중이며 현재 디렉토리의 파일 수를 인쇄하고 싶습니다.
작동하는 코드를 작성했는데
이 (중복) 스크립트를 단순화할 수 있는 방법이 있습니까?
$(ls -l | grep ^- | wc -l) $(if [ $(ls -l | grep ^- | wc -l) -eq 1 ]; then echo "file"; else echo "files"; fi)
내 목적은 폴더의 파일 수를 인쇄하고만약에선언해야 함다루다복수형은 두 가지 상황을 관리합니다.
- 파일 1개
- 파일 2개
예를 들어, my ~ 폴더에는 세 개의 파일이 포함되어 있으므로 스크립트는 "files"라는 단어를 인쇄해야 합니다. my ~/Desktop에는 파일이 하나만 있으므로 스크립트는 "files"를 인쇄해야 합니다.
위의 코드 줄을 작성하고 작업이 완료되었지만 더 깔끔하고 스마트한 방법이 있다고 생각했습니다.
답변1
첫째, 더 빠른 명령을 사용하여 올바른 번호를 찾을 수 있습니다. 내 테스트에서는 이것이 find . -maxdepth 1 -type f -not -name '.*' | wc -l
(약 4:3)보다 빠른 것으로 나타났습니다. ls -l | grep '^-' | wc -l
파일도 숨기려면 찾기 부분을 사용하거나 ls -a
생략 해야 합니다.-not -name '.*'
다음으로, 파일을 두 번 계산하는 대신 결과를 변수에 저장하고 재사용합니다.
$(count=$(find . -maxdepth 1 -type f -not -name '.*' | wc -l)
if [[ "$count" -eq 1 ]]; then
echo "1 file"
else
echo "$count files"
fi)
보시다시피, 서브셸을 사용해야 합니다. 그렇지 않으면 두 번째 명령에서 변수를 사용할 수 없습니다. 나는 또한 여기서 [[ ... ]]
대신 bash 특정 것을 사용했습니다 [ ... ]
. 일반적으로 말하면 이것이 더 나은 솔루션입니다.
마지막으로 Bash 변수를 사용하여 $PROMPT_COMMAND
프롬프트를 인쇄하기 전에 일부 코드를 실행할 수도 있습니다. $PS1
이렇게 하면 모든 코드를 변수에 저장할 필요가 없습니다 $PS1
. 변수 $count
는 다음과 같습니다.글로벌하지만. 다음과 같이 보일 수 있습니다:
function count_files () {
__count=$(find . -maxdepth 1 -type f -not -name '.*' | wc -l)
if [[ "$__count" -eq 1 ]]; then
__plural=
else
__plural=s
fi
}
PROMPT_COMMAND=count_files
PS1='other stuff $__count file$__plural more stuff'
그러나 이러한 단계 중 어떤 단계를 단순화하는 것으로 간주하는지 확실하지 않습니다. :)
편집하다
방금 알아냈어이 스레드. 다음과 같이 파일 수를 계산하는 데 사용할 수 있습니다.
function count_files () {
local name
__count=0
for name in *; do
[[ -f "$name" ]] && ((__count++))
done
# like above ...
}
찾기 버전보다 약 1:13 정도 빠릅니다. 이는 더 적은 수의 프로세스를 시작하기 때문에 남성적입니다(find 버전이 ls 버전보다 빠른 이유와 동일). 트레드에는 다른 extglob 솔루션이 있습니다. 그것은 모두 당신이 파일을 원하는지, 파일에 대한 링크를 원하는지에 따라 달라집니다. 다시 말하지만, 코드는 더 빨라졌지만 이제는 더 복잡해 보입니다. 그렇다면 어떤 종류의 "단순성"을 목표로 하시나요?
편집 2
내가 댓글에서 말한 내용에도 불구하고 프로세스를 시작하는 오버헤드는 정렬해야 하는 경우 정렬하는 오버헤드에 비해 아마도 작을 것입니다.많은/usr/bin/
파일의 경우 약 2500개의 파일이 포함된 코드를 실행하면 표시되기 시작합니다.
답변2
스크립트를 단순화하는 첫 번째 방법은 파일 수를 변수에 저장하여 다시 계산할 필요가 없도록 하는 것입니다.
현재 디렉터리의 파일 수를 찾는 다른 방법:
n=$(ls -l | grep -c ^-)
여기서 단순화는 grep의 -c
옵션을 사용하여 일치 항목을 계산하는 것입니다. 일치 항목을 잘못 계산할 위험이 있습니다.ls의 출력을 구문 분석합니다.라는 파일이 있으면 $'some\n-file'
줄 시작 부분에 하이픈을 넣는 것처럼 보입니다.
n=$(stat -c %F -- * | grep -c 'regular .*file')
grep은 .*
"일반 파일"과 "일반 빈 파일"이 일치하는 것으로 간주합니다. 이것통계자료*
이 명령은 쉘 glob이 피하는 각 파일의 유형을 출력합니다 ls
.
bash에 익숙하지만 들어본 적이 있다면zsh와 강력한 파일 이름 글로빙 구문, 다음을 수행할 수 있습니다.
n=$(zsh -c 'a=( *(.) ); echo ${#a}')
a
그 안에 확장자가 "Pure Files"인 파일 목록으로만 필터링된 파일 로 채워지는 배열을 만듭니다 .*
.
복소수를 올바르게 인쇄하려면 Case 문을 고려하세요.
case $n in
(1) printf "1 file";;
(*) printf "$n files";;
esac
Case 문은 파일 개수에 따라 서로 다른 메시지를 인쇄하려는 경우 더 큰 유연성을 제공합니다(예: "파일 없음!").
더 간단하게는 다음과 같은 조건을 고려해보세요.
[[ $n == 1 ]] && printf "1 file" || printf "$n files"
마지막으로 Steeldriver 권장 사항에 대해 논의합니다.
n=$( files=(*); dirs=(*/); echo $(( ${#files[@]} - ${#dirs[@]} )))
printf "%d file%s" "$n" "$(test "$n" -ne 1 && echo s)"
n
이는 (결국) 명령 대체를 활성화하여 값을 할당합니다. 임시 명령 대체에서는 files
모든 항목과 dirs
디렉터리에만 해당하는 두 개의 배열을 만들었습니다. 명령 대체의 마지막 작업은 둘 사이의 차이점을 보고하는 것입니다. 그런 다음 printf
적절한 복수형 접미사와 함께 파일 수를 인쇄합니다.
이것은 가지고 있는 모든 설정을 사용합니다 dotglob
. 도트 파일을 강제로 계산(또는 생략)하려면 dotglob
명령 대체에서 설정하거나 설정을 해제해야 합니다.
dotglob의 현재 값을 사용합니다.
n=$( files=(*); dirs=(*/); echo $(( ${#files[@]} - ${#dirs[@]} )))
현재 도트 글로브에 관계없이 도트 파일을 강제로 계산합니다.
n=$(shopt -s dotglob;
files=(*); dirs=(*/); echo $(( ${#files[@]} - ${#dirs[@]} )))
구현하다아니요현재 dotglob에 관계없이 도트 파일 수:
n=$(shopt -u dotglob;
files=(*); dirs=(*/); echo $(( ${#files[@]} - ${#dirs[@]} )))
답변3
find . -maxdepth 1 -type f | awk 'END {printf("%d %s%s\n", NR, "file", (NR > 1) ? "s" : "")}'
설명하다:
find . -maxdepth 1 -type f
- 현재 디렉토리의 모든 파일을 검색합니다.awk 'END {printf("%d %s%s\n", NR, "file", (NR > 1) ? "s" : "")}'
NR
= 프로그램으로부터 수신된 라인 수find
, 파일 수와 같습니다.(NR > 1) ? "s" : "")
- 라인(파일) 개수가 1개를 초과하는 경우s
단어 끝에 끝을 추가합니다file
.
답변4
방금 이걸 만들었어요. ls
파일 이름에 이상한 문자(예: 개행 문자)가 있으면 출력이 손상될 수 있기 때문에 경로 이름 확장을 사용 하지 않았습니다 .
#!/usr/bin/env bash
((n=0))
for i in *; do
[[ -f "${i}" ]] && ((n++))
done
((n==1)) && { echo "${n} file"; exit; }
echo "${n} files"