저는 시스템에서 파일을 검색한 다음 각 파일에 대해 몇 가지 온전성 검사를 수행하는 스크립트를 작성 중입니다. 검사를 통과하면 fzf에 표시하고 싶습니다. fzf에서 항목을 클릭하면 프로그래밍 방식으로 파일을 실행하고 싶습니다.
지금까지 나는 다음을 가지고 있습니다 :
dir="/path/to/dir"
fd . $dir --size +1MB | while read -r line; do
file_type=$(file -b "$line")
echo "$line" | fzf
if [[ "$file_type" == "data" ]]; then
echo "$file_type"
fi
done
기본적으로 지정된 디렉터리에서 1MB보다 큰 파일을 검색합니다. 각 파일에 대해 file 명령을 실행하고 명령 출력이 "data"를 반환하는지 확인합니다. 그렇다면 fzf 목록에 추가하고 싶습니다. 그런 다음 항목을 클릭하면 응용 프로그램을 사용하여 전체 경로로 파일을 실행하고 싶습니다. 예: myapp /path/to/file/selected/in/fzf
.
지금까지의 문제는 fzf 블록과 내가 루프의 목록을 채울 수 없다는 것입니다. 정말로 모든 것을 배열에 먼저 추가한 다음 fzf로 파이프해야 합니까? 이상적으로 이는 병렬로 발생해야 합니다. 즉, 검색이 완료될 때까지 기다리지 않고 검색이 진행 중인 동안 동적으로 새 항목을 추가하고 싶습니다.
또한 나중에 선택한 파일을 실행하는 방법도 모르겠습니다. 누군가 나를 도와줄 수 있나요?
답변1
fzf
당신이 생각하는 방식을 방해하지 않을 것입니다. 목록에 동적으로 추가할 수 있습니다. 다음 예에서 이를 확인할 수 있습니다( pv
설치되지 않은 경우 먼저 설치).
find / | pv -qlL 1 | fzf
pv
초당 한 줄을 출력하는 이 줄이 표시되며 차단 fzf
없이 선택 항목을 위아래로 이동할 수 있습니다.
코드의 문제는 루프 fzf
내에서 실행되고 있다는 것입니다 while read …
. 각 행에 대해 fzf
해당 행만 가져오는 별도의 코드를 실행합니다 . 루프는 fzf
완료된 후에만 계속될 수 있습니다. 그러므로 더 읽기를 거부하는 것이 아니라 fzf
더 읽기를 거부하는 것입니다. fzf
루프 내부에 있다는 것입니다 .
기본적으로 다음과 같은 것을 원합니다.
find … | data_filter | fzf
그중에는 data_filter
행을 필터링하는 코드가 있습니다. 쉘 루프일 수 있습니다.
find … | while IFS= read -r line; do …; done | fzf
필터로서 루프는 필터링하고 싶지 않은 모든 행(및 해당 행만)을 표준 출력으로 인쇄해야 합니다. 이름이 지정된 쉘 함수일 수 있습니다 data_filter
. 귀하의 경우 이것이 올바른 기능입니다.
data_filter() {
while IFS= read -r line; do
[ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\n' "$line"
done
}
그런 다음 이것은 파이프의 필터로 사용됩니다.
find … | data_filter | fzf
이제 우리는 잘 작동하고 선택한 경로 이름을 인쇄하는 파이프를 갖게 되었습니다. 파일에 대해 특정 작업을 수행하려면 다음 명령 중 하나를 사용하십시오.
find … | data_filter | fzf | xargs …
,xargs
전체 줄을 있는 그대로 읽도록 구성됩니다. GNU의 경우xargs
실행하려는 도구가 다음과 같은 경우ls -l
:find … | data_filter | fzf | xargs --no-run-if-empty -d '\n' -I{} ls -l {}
하지만
xargs
기본적으로 따옴표를 해석하고 다른 작업(예: 분할)을 수행하므로 이러한 상황에서 사용하려면 해당 옵션을 잘 알아야 합니다. 나는 그것을 마스터한 적이 없으며xargs
여기서 최고의 옵션 세트를 사용하고 있는지 확신할 수 없습니다. 내 요점은 다음과 같습니다. 이와 같은 간단한 호출은… | fzf | xargs ls -l
많은 경우에 중단됩니다.f="$(find … | data_filter | fzf)"
"$file"
, 원하는 곳 어디에서나 사용하세요. 한 가지 장점은 종료 상태를 알 수 있다는 것입니다fzf
. 이론적 단점은$()
후행 줄 바꿈이 제거된다는 것입니다. 실제로 우리의 경우 후행(또는 임의) 개행 문자가 있는 경로 이름은 어쨌든 잘 전달되지 않습니다data_filter | fzf
.예:
f="$(find . | data_filter | fzf)" status="$?" [ "$status" = 0 ] && ls -l "$f"
파이프라인의 모든 도구는 줄 바꿈을 사용하여 항목을 구분하므로 줄 바꿈이 있는 경로 이름은 코드를 손상시킵니다.
줄바꿈이 포함된 경로 이름을 처리하려면 전체 파이프라인에서 (줄바꿈으로 끝나는 대신) Null로 끝나는 항목을 사용하도록 해야 합니다.
먼저
find … -print0
(또는 이와 동등한fd
명령)이 필요합니다. 일부 구현에서는find
이를 지원하지 않습니다-print0
(-exec printf '%s\0' {} +
교체 가능).그러면 새 필터 기능은 다음과 같아야 합니다.
data_filter0() { while IFS= read -r -d '' line; do [ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\0' "$line" done }
다음 번에 사용하세요
fzf --read0 --print0
.마침내
xargs -r0 …
. (대안은$()
번거롭기 때문에 자세히 설명하지 않겠습니다. 이 경우에는 이것이 선호됩니다xargs -r0
.) 이러한 옵션은 이식 가능하지 않으며xargs
지원할 수도 있고 지원하지 않을 수도 있습니다. 보너스로xargs -r0
객관식도 잘 작동합니다fzf -m
.
파이프라인 예:
find . -print0 | data_filter0 | fzf -m --read0 --print0 | xargs -r0 ls -l
참고 사항 및 유용한 링크:
일반적으로 필터를 내장할 수 있습니다
find …
(감사합니다find -exec
). 당신이 사용하고 싶어하는 것 같고fd
도구에 대해 잘 모르기 때문에 그렇게하지 않았습니다 .항목을 일찍(즉,
find
필터가 완료되기 전) 선택하면fzf
파이프라인의 나중 항목이find
종료되기 전에 종료될 수 있습니다. 필터는fzf
쓰기를 시도한 후에만 더 이상 존재하지 않는다는 것을 알 수 있습니다. 마찬가지로find
쓰기를 시도한 후에 알 수 있습니다.이 답변). 이는find
작업이 필요 이상으로 오래 걸릴 수 있음을 의미하며, 이로 인해 셸이 스크립트의 다음 명령을 계속 진행하지 못하게 됩니다(또는 대화형인 경우 프롬프트가 표시되지 않게 됩니다). 백그라운드에서 일부 부분을 실행할 수 있습니다.( find . -print0 | data_filter0 & ) | fzf -m --read0 --print0 | xargs -r0 ls -l
find
종료 후 필터 작동이 중지되지는 않지만fzf
전체 라인이 중지되지는 않습니다. 쉘은ls
작업을 완료한 후 즉시 이동합니다. 백그라운드 프로세스는 조만간 종료됩니다.올바른 인용. 귀하의 질문에 인용된 내용이 없습니다
$dir
.