디렉토리에서 최신 파일을 '꼬리'하는 방법


20

tail에서 디렉토리에 최신 파일을 어떻게 만들 수 있습니까?


1
가까이 다가 오면 프로그래머는 테일링해야합니다!
amit

닫기는 수퍼 유저 또는 서버 오류로 이동하기위한 것입니다. 그 질문은 그곳에 살 것이며, 관심이있는 사람들이 더 많이 찾을 것입니다.
Mnementh

여기서 진짜 문제는 디렉토리에서 가장 최근에 업데이트 된 파일을 찾는 것입니다.이 파일에 이미 응답했다고 생각합니다 (여기 또는 수퍼 유저 중 하나는 기억할 수 없습니다).
dmckee

답변:


24

마십시오 하지 LS의 출력을 구문 분석! ls의 출력을 파싱하는 것은 어렵고 신뢰할 수 없습니다 .

이 작업을 수행해야하는 경우 find를 사용하는 것이 좋습니다. 원래 나는 여기에 간단한 해결책을 제시하기위한 간단한 예를 가지고 있었지만이 대답은 다소 인기가있는 것처럼 보이기 때문에 복사 / 붙여 넣기 및 모든 입력과 함께 사용하기에 안전한 버전을 제공하기 위해 이것을 수정하기로 결정했습니다. 편안하게 앉아 있습니까? 현재 디렉토리의 최신 파일을 제공하는 oneliner로 시작하겠습니다.

tail -- "$(find . -maxdepth 1 -type f -printf '%T@.%p\0' | sort -znr -t. -k1,2 | while IFS= read -r -d '' -r record ; do printf '%s' "$record" | cut -d. -f3- ; break ; done)"

지금은 하나의 라이너가 아닌가? 다음은 다시 셸 함수로 사용되며 읽기 쉽도록 형식화되었습니다.

latest-file-in-directory () {
    find "${@:-.}" -maxdepth 1 -type f -printf '%T@.%p\0' | \
            sort -znr -t. -k1,2 | \
            while IFS= read -r -d '' -r record ; do
                    printf '%s' "$record" | cut -d. -f3-
                    break
            done
}

그리고 지금 oneliner으로 :

tail -- "$(latest-file-in-directory)"

다른 모든 방법이 실패하면 위의 기능을 포함시킬 수 .bashrc있고 한 가지 경고로 해결 된 문제를 고려할 수 있습니다. 당신이 일을 끝내기를 원한다면 더 이상 읽을 필요가 없습니다.

이것에 대한주의 사항은 하나 이상의 줄 바꿈으로 끝나는 파일 이름이 여전히 tail올바르게 전달되지 않는다는 것입니다. 이 문제를 해결하는 것은 복잡하며 이러한 악의적 인 파일 이름이 발견되면 상대적으로 위험한 파일 대신 "No such file"오류가 발생하는 비교적 안전한 동작이 발생하기에 충분하다고 생각합니다.

수분이 많은 세부 사항

궁금한 점은 이것이 작동하는 방식, 왜 안전한지, 왜 다른 방법이 그렇지 않은지에 대한 지루한 설명입니다.

위험, 윌 로빈슨

우선, 파일 경로를 구분하기에 안전한 유일한 바이트는 유닉스 시스템의 파일 경로에서 보편적으로 금지 된 유일한 바이트이기 때문에 null입니다. 파일 경로 목록을 처리 할 때는 null을 구분 기호로만 사용하고 한 파일에서 다른 파일 경로로 단일 파일 경로를 전달할 때 임의 바이트에서 질식하지 않는 방식으로 처리하는 것이 중요합니다. 파일 이름에 줄 바꿈이나 공백이 없다고 가정하여 실패하는 문제와 다른 문제를 해결하는 겉으로는 올바른 방법이 많이 있습니다. 어느 가정도 안전하지 않습니다.

오늘의 목적을위한 첫 번째 단계는 null로 구분 된 파일 목록을 찾을 수 없도록하는 것입니다. GNU와 같은 find지원 이있는 경우 매우 쉽습니다 -print0.

find . -print0

그러나이 목록에는 여전히 최신 정보가 나와 있지 않으므로 해당 정보를 포함시켜야합니다. -printf출력에 표시 할 데이터를 지정할 수있는 find 스위치 를 사용하도록 선택합니다 . 모든 버전의 find지원 -printf(표준이 아님)은 아니지만 GNU find는 지원합니다. -printf당신이 없는 자신을 발견 하면 -exec stat {} \;어느 시점 에 의존 해야 stat표준이 아니므 로 휴대 성의 모든 희망을 포기해야 합니다. 지금은 GNU 도구가 있다고 가정하겠습니다.

find . -printf '%T@.%p\0'

여기서 나는 %T@유닉스 시대가 시작된 이후의 마침표와 그 후의 분수를 나타내는 숫자가 뒤 따르는 수정 시간 인 printf 형식 을 묻습니다 . %pnull 바이트로 끝나기 전에이 다른 기간을 추가 한 다음 (파일의 전체 경로)를 추가합니다.

지금 나 한테있어

find . -maxdepth 1 \! -type d -printf '%T@.%p\0'

말할 것도없이 완료 될 수 있지만 하위 디렉토리의 내용을 나열 -maxdepth 1하지 못하게 하고 원하지 않는 디렉토리는 건너 뜁니다 . 지금까지 수정 시간 정보가있는 현재 디렉토리에 파일이 있으므로 수정 시간을 기준으로 정렬해야합니다.find\! -type dtail

올바른 순서로 가져 오기

기본적 sort으로 입력은 개행으로 구분 된 레코드 일 것으로 예상합니다. GNU를 가지고 있다면 스위치 sort를 사용하여 대신 널로 구분 된 레코드를 요구할 수 있습니다 -z.; 표준의 sort경우 해결책이 없습니다. 나는 처음 두 숫자 (초와 초의 분수)로만 정렬하고 실제 파일 이름으로 정렬하고 싶지 않으므로 sort두 가지를 말하고 싶습니다 . 먼저, .필드 구분 기호 (마침표 ( )를 고려해야 함 ) 둘째, 레코드 정렬 방법을 고려할 때 첫 번째 필드와 두 번째 필드 만 사용해야합니다.

| sort -znr -t. -k1,2

우선, 나는 가치를 함께 취하지 않는 세 가지 짧은 옵션을 묶습니다. -znr간결한 말입니다 -z -n -r). 그 후 -t .(공백은 선택 사항 임) sort필드 구분 문자를 알려주고 -k 1,2필드 번호를 지정합니다 : first 및 second ( sort0이 아닌 1부터 필드를 계산). 현재 디렉토리의 샘플 레코드는 다음과 같습니다.

1000000000.0000000000../some-file-name

즉 , 이 레코드를 주문할 때 sort먼저 확인해야 합니다. 이 옵션은 두 값이 모두 숫자이므로이 값을 비교할 때 숫자 비교를 사용하도록 지시 합니다. 숫자의 길이는 고정되어 있지만 해를 끼치 지 않으므로 중요하지 않을 수 있습니다.10000000000000000000-nsort

다른 스위치 sort-r"reverse"입니다. 기본적으로 숫자 정렬의 출력은 가장 낮은 숫자부터 시작 -r하여 가장 낮은 숫자와 가장 높은 숫자를 먼저 나열하도록 변경합니다. 이 숫자는 타임 스탬프가 높을수록 더 새로운 것을 의미하므로 목록의 시작 부분에 최신 레코드가 저장됩니다.

중요한 부분 만

파일 경로 목록이 나오면 sort이제 원하는 답을 찾을 수 있습니다. 남은 것은 다른 레코드를 삭제하고 타임 스탬프를 제거하는 방법을 찾는 것입니다. 불행하게도, 심지어 GNU는 head하고 tail그들이 널 (null)로 구분 된 입력을 작동 할 수 있도록 스위치를 허용하지 않습니다. 대신 나는 while 루프를 가난한 사람의 일종으로 사용합니다 head.

| while IFS= read -r -d '' record

먼저 IFS파일 목록에 단어 분할이 적용 되지 않도록 설정을 해제했습니다 . 다음으로 나는 read두 가지를 말한다 : 입력 ( -r) 에서 이스케이프 시퀀스를 해석하지 말고 입력은 널 바이트 ( -d)로 구분된다 ; 여기서 빈 문자열 ''은 "구분 기호 없음"을 일명 null로 구분하는 데 사용됩니다. 루프를 반복 할 record때마다 while단일 타임 스탬프와 단일 파일 이름을 갖도록 각 레코드를 변수로 읽어들 입니다. 참고 -dGNU 확장이다; 표준 만있는 read경우이 기술은 작동하지 않으며 약간의 의지가 있습니다.

우리는 알고 record변수가 모든 기간 문자로 구분이 세 부분을 가지고 있습니다. 은 Using cut유틸리티를 그들 중 일부를 추출하는 것이 가능하다.

printf '%s' "$record" | cut -d. -f3-

여기에서 전체 레코드가 printf파이프로 전달 됩니다 cut. bash 에서는 성능 향상 을 위해 here 문자열 을 사용 하여이 작업을 단순화 할 수 cut -d. -3f- <<<"$record"있습니다. 우리는 cut두 가지를 말합니다 : 먼저 , 구분자 를 사용 하는 -d것처럼 필드를 식별하기위한 특정 구분자가 있어야합니다 . 두 번째 는 특정 필드의 값만 인쇄 하도록 지시 합니다. 필드 목록은 세 번째 필드와 모든 다음 필드의 값을 나타내는 범위로 제공됩니다. 즉 , 레코드에서 찾은 초 를 포함하여 모든 것을 읽고 무시한 다음 파일 경로 부분 인 나머지를 인쇄합니다.sort.cut-f3-cut.

최신 파일 경로를 인쇄하면 계속 진행할 필요가 없습니다 break. 두 번째 파일 경로로 이동하지 않고 루프를 종료합니다.

남아있는 유일한 것은 tail이 파이프 라인이 반환 한 파일 경로에서 실행 중 입니다. 필자의 예제에서 파이프 라인을 서브 쉘로 묶어이 작업을 수행했음을 알 수 있습니다. 눈치 채지 못한 것은 서브 쉘을 큰 따옴표로 묶었다는 것입니다. 파일 이름을 안전하게 보호하기 위해이 모든 노력을 기울여도 인용되지 않은 서브 쉘 확장으로 인해 여전히 문제가 발생할 수 있기 때문에 이것은 중요합니다. 자세한 설명은 당신이 관심이 있다면 사용할 수 있습니다. 두 번째 중요하지만 간과하기 쉬운 측면 은 파일 이름을 확장하기 전에 tail옵션 --을 제공했다는 것 입니다. 이것은 지시합니다tail더 이상 옵션을 지정하지 않고 다음에 나오는 모든 파일 이름은 파일 이름이므로로 시작하는 파일 이름을 안전하게 처리 할 수 ​​있습니다 -.


1
@AakashM : 파일에 이름에 "비정상적인"문자가있는 경우 (예 : 거의 모든 문자가 합법적) "놀라운"결과를 얻을 수 있기 때문에
John Zwinck

6
파일 이름에 특수 문자를 사용하는 사람들은 모든 것을 얻을 자격이 있습니다. –-)

6
paxdiablo가 그 말을 충분히 아프게하는 것을 보면서 두 사람이 투표했습니다! 버그가있는 소프트웨어를 작성하는 사람들은 의도적으로 모든 것을 얻을 자격이 있습니다.
John Zwinck

4
따라서 위의 해결 방법은 찾기에 -printf 옵션이 없기 때문에 osx에서 작동하지 않지만 다음은 stat 명령의 차이로 인해 osx에서만 작동합니다. 아마도 여전히 누군가를 도울 것입니다.tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
audio.zoom

2
"불행하게도 심지어 GNU headtail그들이 널 (null)로 구분 된 입력을 작동 할 수 있도록 스위치를 허용하지 않습니다." 에 대한 나의 교체 head: … | grep -zm <number> "".
Kamil Maciorowski

22
tail `ls -t | head -1`

공백이있는 파일 이름이 걱정된다면

tail "`ls -t | head -1`"

1
그러나 최신 파일에 공백이나 특수 문자가 있으면 어떻게됩니까? 이 문제를 피하려면``대신 $ ()를 사용하고 서브 쉘을 인용하십시오.
phogg

나는 이것을 좋아한다. 깨끗하고 간단합니다. 그렇습니다.

6
강력하고 올바른 것을 희생하면 깨끗하고 간단합니다.
phogg

2
글쎄, 그것은 당신이하고있는 일에 달려 있습니다. 가능한 모든 파일 이름에 대해 항상 모든 곳에서 작동하는 솔루션은 매우 좋지만 제한된 상황 (예 : 이상한 이름이 아닌 로그 파일)에서는 불필요 할 수 있습니다.

이것은 지금까지 가장 깨끗한 솔루션입니다. 감사합니다!
demisx

4

당신이 사용할 수있는:

tail $(ls -1t | head -1)

$()구조는 명령을 실행하는 하위 쉘 시작 ls -1t이를 통해 배관 (모든 시간 순서로 파일을 한 줄에 하나씩 나열) head -1첫 번째 줄 (파일)을 얻을합니다.

그런 다음 해당 명령 (가장 최근 파일)의 출력이 전달되어 tail처리됩니다.

가장 최근에 작성된 디렉토리 항목 인 경우 디렉토리를 가져올 위험이 있습니다. 별칭으로 트릭을 사용하여 해당 로그 파일 만 포함 된 디렉토리에서 가장 최근의 로그 파일 (회전 집합의)을 편집했습니다.


-1필요하지 않습니다 ls이 파이프에 때 당신을 위해 작업을 수행합니다. 비교 lsls|cat예를 들어,.
추후 공지가있을 때까지 일시 중지되었습니다.

Linux에서는 그럴 수 있습니다. "진정한"유닉스에서 프로세스는 출력이 진행되는 위치에 따라 동작을 변경하지 않았습니다. 파이프 라인 디버깅이 정말 성 가실 것입니다 :-)

흠, 맞습니다. ISTR은 필터를 통해 출력을 파이핑 할 때 4.2BSD에서 열 형식 출력을 얻기 위해 "ls -C"를 발행해야하며 Solaris에서 ls가 동일한 방식으로 작동한다고 확신합니다. 어쨌든 "하나의 진정한 유닉스"는 무엇입니까?

인용 부호! 인용 부호! 파일 이름에는 공백이 있습니다!
노먼 램지

@TMN : 진정한 유닉스 방식 중 하나는 인간이 아닌 소비자에게 ls에 의존하지 않는 것입니다. "출력이 터미널로 출력되는 경우 형식이 구현 정의됩니다." -이것은 사양입니다. 확실하게하려면 ls -1 또는 ls -C라고 말해야합니다.
phogg

4

POSIX 시스템에서는 "마지막으로 생성 된"디렉토리 항목을 얻는 방법이 없습니다. 각 디렉토리 항목이 atime, mtime그리고 ctime하지만, 마이크로 소프트 윈도우에 반하는은은 ctime"마지막 상태 변경의 시간"평균 CREATIONTIME을하지 않습니다,하지만.

그래서 당신이 얻을 수있는 최선의 방법은 "최근에 마지막으로 수정 된 파일을 맞추는 것"입니다. 이것은 다른 답변에서 설명합니다. 나는이 명령에 갈 것입니다 :

tail -f "$ (ls -tr | sed 1q)"

ls명령 주위에 따옴표를 적어 둡니다 . 스 니펫은 거의 모든 파일 이름으로 작동합니다.


잘 했어. 바로 포인트. +1
Norman Ramsey 2016 년

4

시계를 사용할 수있는 파일 크기 변경을보고 싶습니다.

watch -d ls -l

3

에서 zsh:

tail *(.om[1])

다음을 참조하십시오 : http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , 여기에서 m수정 시간을 나타내며 m[Mwhms][-|+]n, 앞의 o방법은 한 방법으로 정렬됨을 의미합니다 ( O다른 방법으로 정렬). .유일한 수단 일반 파일. 괄호 안에서 [1]첫 번째 항목을 선택합니다. [1,3]가장 오래된 사용을 얻기 위해 세 가지 사용을 선택 합니다 [-1].

짧고 사용하지 않습니다 ls.


1

이 작업을 수행하는 백만 가지 방법이있을 수 있지만 내가하는 방법은 다음과 같습니다.

tail `ls -t | head -n 1`

백틱 사이의 비트 (문자와 같은 따옴표)가 해석되고 결과가 tail로 리턴됩니다.

ls -t #gets the list of files in time order
head -n 1 # returns the first line only

2
백틱은 악하다. 대신 $ ()를 사용하십시오.
윌리엄 퍼셀

1

간단한:

tail -f /path/to/directory/*

나를 위해 잘 작동합니다.

tail 명령을 시작한 후 생성되는 파일을 얻는 것이 문제입니다. 그러나 필요하지 않으면 (위의 모든 솔루션이 신경 쓰지 않기 때문에) 별표는 더 간단한 솔루션 인 IMO입니다.



0

누군가가 그것을 게시 한 다음 어떤 이유로 든 지 웠지만 이것이 유일하게 작동합니다.

tail -f `ls -tr | tail`

디렉토리를 제외해야합니까?
amit

1
나는 이것을 원래 게시했지만 Sorpigal에 동의 한 이후로 결과를 분석 ls하는 것이 가장 현명한 일이 아니라는 것을 동의 한 이후에 삭제했습니다 .
ChristopheD

빠르고 더러운 디렉토리가 필요합니다. 당신이 당신의 대답을 추가 할 것입니다 경우에 따라서, 그 하나 받아 들일 것
Itay Moav -Malimovka

0
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`

설명:

  • ls -lt : 수정 시간별로 정렬 된 모든 파일 및 디렉토리 목록
  • grep -v ^ d : 디렉토리 제외
  • head -2 이후 : 필요한 파일 이름 구문 분석

1
영리한 경우 +1, ls 출력 구문 분석의 경우 -2, 서브 쉘을 인용하지 않는 경우 -1, 마술 "필드 8"가정 (이동 가능하지 않음)의 경우 -1, 너무 영리한 경우 -1 . 총점 : -4.
phogg

@Sorpigal 합의. 그래도 나쁜 모범이되어 기쁘다.
amit

네, 그것이 너무나 많은 계산에서 잘못 될 것이라고 상상하지 못했습니다
amit

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.