테스트 사례: (루트가 000 권한을 무시하므로 루트가 아닌 사용자를 사용하십시오...)
#in a clean directory:
[ -f file_1 ] && chmod 600 file_? # for repeat tests...
for i in file_1 file_2 file_3; do
printf 'A\nB\n' > "$i"
# we need at least 1 char : awk/gawk silently skips empty files...
done
chmod 000 file_2
awk '(FNR==1) { print FILENAME }' file_?
# tried with : regular (old unixes) awk on AIX. and gawk on Linux.
# the fatal "permission denied" on file_2 stops [g]awk.
치명적인 오류를 포착하고 다음 파일을 계속 처리할 수 있는 방법이 있습니까?
(그렇지 않으면 걱정스럽습니다. 여러 파일에 awk를 사용한다고 해서 모든 파일이 처리된다는 보장은 없습니다. 파일 중 하나라도 읽을 수 없으면 치명적으로 종료되기 때문입니다.)
가능하다면: 답변해주세요
- 일반 awk의 경우,
- 그리고 바보
- 다른 관련 awk 버전이 있습니까? (안돼? 등)
답변1
GAWK를 사용하면:
gawk 'BEGINFILE { if (ERRNO) nextfile } (FNR==1) { print FILENAME }' file_?
BEGINFILE
블록 에서는 ERRNO
파일이 성공적으로 열리면 비어 있습니다. nextfile
이는 다음 파일로 점프하고 오류로 인해 종료되는 것을 방지하는 데 사용할 수 있습니다.
나는 AWK의 다른 구현이 이것을 지원한다고 생각하지 않습니다.
이식 가능하게는 모든 인수를 반복하여 읽을 수 없는 파일을 가리키는지 확인하고, 그렇다면 AWK가 수동으로 처리를 시작하기 전에 인수에서 해당 인수를 제거할 수 있습니다.구현 예가 있습니다.. 그러나 이 루프를 사용하여 검사된 파일은 AWK가 처리를 시작하기 전에 읽을 수 없게 될 수 있으므로(그 반대의 경우도 마찬가지) 위험합니다.
답변2
@StephenKitt와 @ilkkachu가 gawk 매뉴얼에서 이미 지적했듯이일부 코드가 포함되어 있습니다이렇게 하면 섹션에서 읽을 수 없는 파일이 제거되지만 ARGV[]
테스트 BEGIN
파일 과 실제로 해당 내용을 읽으려고 시도하는 awk 사이에 경쟁 조건이 있습니다. 이전 파일이 큰 경우 훨씬 늦어질 수 있습니다.
gawk 매뉴얼의 스크립트나 gawk가 있는 경우 gawk 매뉴얼 스크립트가 더 깔끔하고 짧고 간단하며 효율적이기 때문에 실제로 경쟁 조건 문제가 있을 수 있다고 생각하지 않는 한 @StephenKitt의 답변에 있는 스크립트를 사용하겠습니다. . 아래 것보다 낫습니다. 임시 파일과 전역 변수가 필요하지 않지만 경쟁 조건이 걱정되는 사람들을 위한 것입니다. 이것은 모든 awk에서 작동하고 시도하기 전에 임시 파일을 생성하는 데 의존하는 더 복잡한 스크립트입니다. 실제 파일 열기 즉시 다가오는 실제 파일을 읽을 수 있는지 여부를 테스트하십시오.
$ cat skip.awk
function addTmp( cmd, oArgv, i, j) {
cmd = "mktemp"
cmd | getline TmpChkFile
close(cmd)
if ( TmpChkFile != "" ) {
print "" > TmpChkFile
close(TmpChkFile)
for (i in ARGV) {
oArgv[i] = ARGV[i]
}
oArgc = ARGC
ARGC = 1
for (i = 1; i < oArgc; i++) {
if ( ! (oArgv[i] ~ /^[a-zA-Z_][a-zA-Z0-9_]*=.*/ \
|| oArgv[i] == "-" || oArgv[i] == "/dev/stdin") ) {
# not assignment or standard input so a file name
ARGV[ARGC] = TmpChkFile
ArgFileNames[++j] = oArgv[i]
ArgFileIndices[j] = ++ARGC
}
ARGV[ARGC++] = oArgv[i]
}
}
}
function rmvTmp() {
system("rm -f \047" TmpChkFile "\047")
}
function chkTmp( stderr, line) {
if ( (FNR == 1) && (FILENAME == TmpChkFile) ) {
++TmpFileNr
if ( (getline line < ArgFileNames[TmpFileNr]) < 0 ) {
stderr = "cat>&2"
printf "Warning: skipping unreadable file \"%s\"\n", ArgFileNames[TmpFileNr] | stderr
close(stderr)
delete ARGV[ArgFileIndices[TmpFileNr]]
}
close(ArgFileNames[TmpFileNr])
next
}
}
BEGIN { addTmp() }
END { rmvTmp() }
{ chkTmp() }
awk가 여러 -f
매개변수를 지원하는 경우(예:POSIX) 또는 여러 스크립트를 동시에 실행하는 다른 방법(예: GNU awk has @include
)을 사용하여 위의 내용을 실제 스크립트에 포함할 수 있습니다(그렇지 않으면 위의 내용을 동일한 파일에 복사/붙여넣기). 스크립트는 다음과 같습니다:
$ cat tst.awk
FNR == 1 { print FILENAME, $0 }
그리고 다음과 같은 파일:
$ ls file_{1..3}
ls: cannot access 'file_2': No such file or directory
file_1 file_3
그런 다음 POSIX awk(그리고 대부분의 (전부는 아니지만) 다른 것)를 사용하여 다음을 수행할 수 있습니다.
$ awk -f skip.awk -f tst.awk file_{1..3}
file_1 A
Warning: skipping unreadable file "file_2"
file_3 C
위의 대부분은 BEGIN
첫 번째 입력 파일이 열리기 전에 한 번 호출하여 ARGV[]
각 실제 입력 파일 앞에 읽을 수 있는 임시 파일이 있는지 확인한 다음 chkTmp()
각 입력 줄에 대해 호출하는 방식으로 작동합니다. 단, 첫 번째인 경우에만 수행합니다. 임시 파일의 줄을 입력하고 열어 보십시오 ARGV[]
. 그런 다음 END
임시 파일을 삭제하십시오. 따라서 실제 오버헤드는 chkTmp()
각 입력 라인에 대한 호출 및 테스트입니다.FNR==1
chkTmp()
모든 Unix 시스템에 파일이 존재한다고 보장할 수 없기 때문에 기존 파일을 사용하는 대신 임시 파일을 생성하고 있습니다. 만약 존재한다고 하더라도, 파일을 읽어야 하는 추가 오버헤드를 피하기 위해 정확히 한 줄 길이가 되어야 합니다. 모든 awks가 이를 지원하는 것은 아니기 때문입니다 (또는 내부에서 nextfile
대신 호출할 수 있습니다 ).next
chkTmp()