최신 파일 일치 패턴을 찾는 Bash 기능


141

Bash에서는 특정 패턴과 일치하는 최신 파일의 파일 이름을 반환하는 함수를 만들고 싶습니다. 예를 들어 다음과 같은 파일 디렉토리가 있습니다.

Directory/
   a1.1_5_1
   a1.2_1_4
   b2.1_0
   b2.2_3_4
   b2.3_2_0

'b2'로 시작하는 최신 파일을 원합니다. bash에서 어떻게해야합니까? 내 ~/.bash_profile스크립트에 이것을 가지고 있어야합니다 .


4
자세한 답변 힌트는 superuser.com/questions/294161/… 을 참조하십시오 . 정렬은 최신 파일을 얻는 핵심 단계
Wolfgang Fahl

답변:


229

ls명령에는 -t시간별로 정렬 할 매개 변수 가 있습니다. 그런 다음을 사용하여 첫 번째 (최신)를 가져올 수 있습니다 head -1.

ls -t b2* | head -1

그러나주의하십시오 : 왜 ls의 출력을 파싱해서는 안됩니까?

내 개인적인 의견 : 파싱 ls은 파일 이름에 공백이나 줄 바꿈과 같은 재미있는 문자가 포함될 수있는 경우에만 위험합니다. 파일 이름에 재미있는 문자가 포함되어 있지 않다고 보장하면 구문 분석 ls이 매우 안전합니다.

많은 다른 상황에서 많은 시스템에서 많은 사람들이 실행할 스크립트를 개발하고 있다면 구문 분석하지 않는 것이 좋습니다 ls.

"올바르게"하는 방법은 다음과 같습니다 . 디렉토리에서 최신 (최신, 초기, 가장 오래된) 파일을 어떻게 찾을 수 있습니까?

unset -v latest
for file in "$dir"/*; do
  [[ $file -nt $latest ]] && latest=$file
done

8
다른 사람을 참고하십시오 : 디렉토리에 대해이 작업을 수행하는 경우, 'ls -td <pattern> | head -1 '
ken.ganong

5
구문 분석 LS의 링크는이 작업을 수행하지 말합니다와의 방법을 권장합니다 BashFAQ 99 . 스크립트에 포함 할 방탄이 아닌 1 라이너를 찾고 있으므로 @lesmana와 같이 ls를 안전하지 않게 계속 구문 분석합니다.
Eponymous

1
@Eponymous : 당신이 깨지기를 사용하지 않고 하나의 라이너를 찾고 있다면 ls, printf "%s\n" b2* | head -1당신을 위해 그것을 할 것입니다.
David Ongaro

2
@DavidOngaro 파일 이름이 버전 번호라는 질문은 없습니다. 이것은 수정 시간에 관한 것입니다. 파일 이름 가정 b2.10_5_2으로도이 솔루션을 죽입니다.
Eponymous

1
귀하의 한 라이너가 정답을 제공하지만 "올바른"방법은 실제로 가장 오래된 파일을 제공하는 것 입니다. 왜 그런지 알아?
NewNameStat

15

의 조합 findls잘 작동

  • 줄 바꿈이없는 파일 이름
  • 매우 많은 양의 파일
  • 그리 길지 않은 파일 이름

해결책:

find . -name "my-pattern" -print0 |
    xargs -r -0 ls -1 -t |
    head -1

그것을 분해하자 :

다음과 find같이 흥미로운 파일을 모두 일치시킬 수 있습니다.

find . -name "my-pattern" ...

그런 다음 -print0모든 파일 이름을 다음 ls과 같이 안전하게 전달할 수 있습니다 .

find . -name "my-pattern" -print0 | xargs -r -0 ls -1 -t

추가 find검색 매개 변수 및 패턴을 여기에 추가 할 수 있습니다

find . -name "my-pattern" ... -print0 | xargs -r -0 ls -1 -t

ls -t수정 시간 (가장 먼저)별로 파일을 정렬하여 한 줄에 하나씩 인쇄합니다. -c생성 시간을 기준으로 정렬하는 데 사용할 수 있습니다 . 참고 : 이것은 줄 바꿈이 포함 된 파일 이름으로 중단됩니다.

마지막으로 head -1정렬 목록에서 첫 번째 파일을 가져옵니다.

참고 : xargs 인수 목록의 크기에 시스템 제한을 사용하십시오. 이 크기를 초과하면 여러 번 xargs호출 ls됩니다. 이것은 정렬과 아마도 최종 출력을 깨뜨릴 것입니다. 운영

xargs  --show-limits

시스템의 한계를 확인하십시오.

참고 2 :find . -maxdepth 1 -name "my-pattern" -print0 하위 폴더를 통해 파일을 검색하지 않으려면 사용 하십시오.

참고 3 : @ starfry-ar에 의해 지적 된 바와 같이에 -r대해 일치하는 파일이없는 경우에 xargs대한 호출을 막는 ls -1 -tfind입니다. 제안 해 주셔서 감사합니다.


2
ls 기반 솔루션보다 낫습니다. 파일이 매우 많은 디렉토리에서 작동하기 때문에 ls가 질식합니다.
Marcin Zukowski

find . -name "my-pattern" ... -print0제공find: paths must precede expression: `...'
Jaakko

오! ..."추가 매개 변수"를 나타냅니다. 필요하지 않으면 생략하십시오.
Boris Brodski

2
패턴과 일치하는 파일이 없으면 패턴과 일치하지 않는 파일을 리턴 할 수 있음을 발견했습니다. find는 xargs에 아무것도 전달하지 않고 파일 목록없이 ls를 호출하여 모든 파일에서 작동하기 때문에 발생합니다. 해결 방법은 -rxargs 명령 줄 에 추가 하여 xargs가 표준 입력에서 아무 것도받지 않으면 명령 줄을 실행하지 않도록 지시하는 것입니다.
starfry

@starfry 감사합니다! 잘 잡았다. 나는 -r대답에 덧붙였다 .
Boris Brodski

7

이것은 필요한 Bash 함수의 가능한 구현입니다.

# Print the newest file, if any, matching the given pattern
# Example usage:
#   newest_matching_file 'b2*'
# WARNING: Files whose names begin with a dot will not be checked
function newest_matching_file
{
    # Use ${1-} instead of $1 in case 'nounset' is set
    local -r glob_pattern=${1-}

    if (( $# != 1 )) ; then
        echo 'usage: newest_matching_file GLOB_PATTERN' >&2
        return 1
    fi

    # To avoid printing garbage if no files match the pattern, set
    # 'nullglob' if necessary
    local -i need_to_unset_nullglob=0
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then
        shopt -s nullglob
        need_to_unset_nullglob=1
    fi

    newest_file=
    for file in $glob_pattern ; do
        [[ -z $newest_file || $file -nt $newest_file ]] \
            && newest_file=$file
    done

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was
    # set by this function
    (( need_to_unset_nullglob )) && shopt -u nullglob

    # Use printf instead of echo in case the file name begins with '-'
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file"

    return 0
}

Bash 내장만을 사용하며 이름에 개행 문자 또는 기타 특이한 문자가 포함 된 파일을 처리해야합니다.


1
당신은 사용 nullglob_shopt=$(shopt -p nullglob)하고 나중에 이전의 $nullglob상태를 되돌릴 수 있습니다 nullglob.
gniourf_gniourf

@gniourf_gniourf가 $ (shopt -p nullglob)를 사용하라는 제안은 좋은 것입니다. 일반적으로 명령 $()이 내장을 사용하는 경우에도 특히 Cygwin에서 느리기 때문에 명령 대체 ( 또는 백틱)를 사용하지 마십시오 . 또한 명령이 실행되는 서브 쉘 컨텍스트로 인해 명령이 예기치 않은 방식으로 작동 할 수 있습니다. 또한 변수 nullglob_shopt값을 잘못 얻는 경우 매우 나쁜 일이 발생할 수 있으므로 명령을 변수 (예 :)에 저장하지 마십시오 .
pjh

간과 할 때 모호한 실패로 이어질 수있는 세부 사항에 관심을 가져 주셔서 감사합니다. 감사!
Ron Burk

문제를 해결하기 위해 더 독특한 방법으로 갔다는 것을 좋아합니다! 유닉스 / 리눅스에는 ' cat! 더 많은 작업이 필요하더라도 사람들 개념을 보여주는 이점이 있습니다. +1하세요!
Pryftan

3

비정상적인 파일 이름 (예 : 유효한 \n문자를 포함하는 파일) 은 이런 종류의 구문 분석으로 혼란을 초래할 수 있습니다.

perl -le '@sorted = map {$_->[0]} 
                    sort {$a->[1] <=> $b->[1]} 
                    map {[$_, -M $_]} 
                    @ARGV;
          print $sorted[0]
' b2*

그것은 그곳에서 사용 된 슈바르츠 식 변환 입니다.


1
슈워츠가 함께하길 바랍니다!
Nathan Monteleone 2016 년

이 답변은 효과가 있지만 문서가 잘못되어서 신뢰할 수는 없습니다.
Wolfgang Fahl

1

stat파일 글로브와 함께 사용할 수 있으며 파일 시간이 앞면에 추가 된 데코 레이트-소트-장식되지 않습니다 :

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

아니. "stat : '% m % t % N'에 대한 파일 시스템 정보를 읽을 수 없습니다 : 해당 파일이나 디렉토리가 없습니다"
Ken Ingram

stat옵션을 올바르게 기억하고 있다면 Mac / FreeBSD 버전의 것일 수 있습니다 . 다른 플랫폼에서 유사한 결과를 얻으려면 다음을 사용할 수 있습니다.stat -c $'%Y\t%n' b2* | sort -rn | head -n1 | cut -f2-
Jeffrey Cash

1

find ... xargs ... head ...위 의 솔루션 을 원 하지만 기능 양식을 사용하기 쉬운 사람들을위한 다크 매직 기능 주문은 다음과 같이 생각할 필요가 없습니다.

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

인쇄물:

file2.txt

어느 것이 :

주어진 디렉토리에서 파일의 가장 오래된 수정 된 타임 스탬프를 가진 파일 이름이 주어진 패턴과 일치합니다.


1

find 명령을 사용하십시오.

Bash 4.2 이상을 사용한다고 가정하면 -printf '%T+ %p\n'파일 타임 스탬프 값에 사용하십시오.

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

예:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

보다 유용한 스크립트를 보려면 https://github.com/l3x/helpers에서 최신 스크립트 찾기를 참조 하십시오.


공백이 포함 된 파일 이름으로 작업하려면 cut -d ''-f2,3,4,5,6,7,8,9 ...
valodzka

0

이를 달성하는 훨씬 더 효율적인 방법이 있습니다. 다음 명령을 고려하십시오.

find . -cmin 1 -name "b2*"

이 명령은 "b2 *"에서 와일드 카드 검색을 사용하여 정확히 1 분 전에 생성 된 최신 파일을 찾습니다. 지난 이틀 동안의 파일을 원한다면 아래 명령을 사용하는 것이 좋습니다.

find . -mtime 2 -name "b2*"

"." 현재 디렉토리를 나타냅니다. 도움이 되었기를 바랍니다.


9
실제로 "최신 파일 일치 패턴"을 찾지는 않습니다. 1 분 전에 생성되었거나 2 일 전에 수정 된 패턴과 일치하는 모든 파일 만 찾습니다.
GnP

이 답변은 제기 된 질문에 기초한 것입니다. 또한 명령을 조정하여 하루 전에 온 최신 파일을 볼 수 있습니다. 당신이하려는 일에 달려 있습니다.
Naufal

"뒤틀림"은 답이 아닙니다. 이것을 "답으로 찾기 명령을 조정하고 원하는 것에 따라 답을 찾으십시오"라고 답하는 것과 같습니다.
Kennet Celeste

불필요한 설명이 확실하지 않습니다. 내 대답이 확실하지 않다고 생각되면 예가 내 대답이 이해되지 않는 이유를 제시하십시오. 그렇게 할 수 없으면 더 이상 언급하지 마십시오.
Naufal

1
솔루션에는 최신 파일이 언제 만들어 졌는지 알아야 합니다. 그것은 질문에 없었으므로 대답은 제기 된 질문에 근거하지 않습니다.
Bloke Down The Pub
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.