매개변수를 전달하려고 하는 쉘 스크립트가 있는데 스크립트에서 빈 출력이 표시됩니다 date
.ARGV[1]
이것은 명령입니다:
#!/bin/bash
dt=$(date -d "yesterday" '+%m%d%Y')
cat /tmp/log.$AUTOSERVE.$dt \
| perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' \
| grep -E '(SUCCESS|FAILURE|TERMINATED)' \
| cut -f2 \
| sort \
| uniq -c \
| perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD \
> /tmp/output.txt
내가 뭘 잘못했나요?
여기서 내가 하고 싶은 일을 설명하겠습니다.
우리는 매일 다음 이름으로 로그 파일을 생성합니다.
log.$AUTOSERVE.mmddyyyy
로그 파일에는 다음 데이터가 포함됩니다.
더 나은 이해를 위해 입력 날짜를 변경했습니다.
Time Message
____________________________________________
[11/16/2023 07:13:45] CAUAJM_I_12345 The application has rollover
[11/16/2023 07:13:45] CAUAJM_I_11111 The machine 111.test.com has lost connection
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: FAILURE JOB: ABC MACHINE: 111.test.com EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 [222.test.com connected to ABC]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS ALARM: JOBFAILURE JOB: ABC EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: TERMINATED JOB: XYZ MACHINE: 222.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: STARTING JOB: 123 MACHINE: 333.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: 456 MACHINE: 444.test.com EXITCODE: 0
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: ABC123 MACHINE: 555.test.com
[11/16/2023 07:13:45] CAUAJM_I_40245 [222.test.com connected to ABC]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: FAILURE JOB: ABC MACHINE: 111.test.com EXITCODE: 1
[11/16/2023 07:13:45] CAUAJM_I_40245 [333.test.com connected to 123]
[11/16/2023 07:13:45] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: TERMINATED JOB: XYZ MACHINE: 222.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: STARTING JOB: 123 MACHINE: 333.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: 456 MACHINE: 444.test.com
[11/16/2023 07:13:46] CAUAJM_I_40245 EVENT: CHANGE_STATUS STATUS: SUCCESS JOB: ABC123 MACHINE: 555.test.com EXITCODE: 0
이 쉘 스크립트는 log
이 파일의 MACHINE 및 STATUS 검색 문자열을 필터링하고 각 시스템에서 실행 중인 작업 수를 계산합니다.
내가 얻는 결과는 다음과 같습니다.
NP2 111.test.com 2
NP2 222.test.com 2
NP2 444.test.com 2
NP2 555.test.com 2
$date_YYYYMMDD
로 바꿔보려고 했는데$dt:
cat /tmp/log.$AUTOSERVE.dt \
| perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' \
| grep -E '(SUCCESS|FAILURE|TERMINATED)' \
| cut -f2 \
| sort \
| uniq -c \
| perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", $ARGV[1], $ENV{AUTOSERV}, $2, $1) . "\n"' $dt \
> /tmp/output.txt
하지만 다음과 같은 오류가 발생합니다.
Can't open 11152023: No such file or directory.
$AUTOSERVE
이 출력에 값을 제공하는 환경 변수가 있다고 가정할 때 NP2
예상되는 결과는 다음과 같습니다.
11152023 NP2 111.test.com 2
11152023 NP2 222.test.com 2
11152023 NP2 444.test.com 2
11152023 NP2 555.test.com 2
답변1
비슷한 것을 원하는 것 같습니다.
#! /bin/sh -
DT=$(date -d yesterday +%m%d%Y) || exit
export DT
exec perl -lne '
if (
($status, $machine) = /STATUS:\s+(\w+).+MACHINE:\s+(\w+\.\w+\.\w+)$/ and
$status =~ /^(SUCCESS|FAILURE|TERMINATED)\z/
) {$count{$machine}++}
END {
for (keys %count) {
print join "\t", $ENV{DT}, $ENV{AUTOSERVE}, $_, $count{$_};
}
}' < ~/tmp/log."$AUTOSERVE.$dt" > ~/tmp/output.txt
노트:
예를 들어, 고정된 이름을 가진 파일은 누구나 쓰기 가능한 디렉터리에서 사용하면 안 됩니다.
/tmp
따라서 여기로 전환하거나 또는 //... 의 전용 영역을 사용하세요~/tmp
./var
~/var
~/.local
$XDG_RUNTIME_DIR
이 코드에는 bash와 관련된 내용이 없으므로 bash 종속성을 추가할 필요가 없습니다.
추가
-n
매개변수는perl
스크립트에 대한 입력입니다.Chris가 이미 말했듯이 귀하의 인용문에 문제가 있습니다.
당신은
AUTOSERV
/AUTOSERVE
차이가 있습니다..
단일 문자와 일치하는 정규식 연산자입니다. 텍스트 포인트를 일치시키려면\.
또는 를 사용하십시오 .[.]
사용법은
date
GNU에 따라 다릅니다. 모든date
구현이-d
옵션을 지원하는 것은 아니며, 지원하는 경우 BSD의 항목이나 인식하지 못하는 항목 ( 비지박스 또는 장난감 상자yesterday
등 )과 같이 전혀 관련이 없는 항목에 사용될 수 있습니다. 이 스크립트를 GNU가 아닌 시스템으로 이식해야 하는 경우 날짜 조작도 가능합니다.date
perl
다음과 같이 단일 정규 표현식을 사용하도록 이를 쉽게 변경할 수 있습니다.
/STATUS:\s+(?:SUCCESS|FAILURE|TERMINATED)\b.+MACHINE:\s+(\w+.\w+.\w+)$/
기계 목록을 사전순으로 정렬하려면
keys %count
를 로 바꾸십시오.sort cmp, keys %count
exec
이와 같은 래퍼 스크립트에서 매우 일반적인 것은 프로세스를 저장하는 것입니다.perl
하위 프로세스에서 실행하고 기다리는 대신 동일한 프로세스에서 실행되도록 쉘에 지시합니다 .cmd
+fork()
& 를exec(cmd)
수행 하지만wait(child)
(exec cmd
아마도 이라고 불러야 할 것임nofork cmd
)exec(cmd)
입력하는 데 시간이 더 오래 걸리더라도 시스템 실행이 더 간단하고 짧고 리소스를 덜 사용한다는 것입니다.%m%d%Y
타임스탬프 형식을 선택하는 것은 좋지 않습니다. 모호하며 어휘 순서(의 출력에서와 같이ls
)가 연대순과 일치하지 않습니다.%Y-%m-%d
아니면%F
보편적으로 인식되고 어휘적으로 연대순(적어도 0001에서 9999까지)으로 정렬되므로 더 좋습니다.cat
파일을 연결하는 명령입니다. 파일에 사용하는 것은 의미가 없습니다.cmd < input > output
(또는 ,<input cmd >output
그러나아니요cmd > output < input
input
) 에는 읽기 위해 열 수 없는 경우cmd
실행되지 않고output
삭제되지 않는다는 추가 이점이 있습니다 .
1 예를 들어 여기에 -MPOSIX
a BEGIN{@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t}
또는 해킹으로 추가하세요 -M'POSIX;@t = localtime; $t[3]--; $dt = strftime "%m%d%Y", @t'
.
답변2
내 마음 속에 떠오르는 질문은 두 번째 perl
줄입니다.
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", '$ARGV[1]', $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
$ARGV[1]
쉘이 구문 분석을 시도할 수 있도록 적시에 작은따옴표를 사용할 수 있습니다 . 일반적으로 $ARGV
쉘 변수는 설정되지 않으므로 전달되는 결과 행은 perl
다음과 같습니다.
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", [1], $ENV{AUTOSERV}, $2, $1) . "\n"' $date_YYYYMMDD
이는 구문적으로 유효하지만 유용할 가능성은 낮으므로 오류가 발생하지 않습니다.
줄 중간에 있는 두 개의 작은따옴표를 제거하면 원하는 것과 거의 비슷한 결과를 얻을 수 있습니다. -n
읽으려면 명시적 루프를 교체해야 합니다.표준 입력@ARGV
귀하의 가치를 포착할 수 있도록 . 포함된 목록은 @ARGV
처음부터 시작되므로 그 목록도 변경했습니다.
perl -e 'while (<STDIN>) { chomp; /^\s+(\d+)\s+(.*)$/ && print join("\t", $ARGV[0], $ENV{AUTOSERV}, $2, $1) . "\n" }' "$date_YYYYMMDD"
소스 파일을 가져와 지정된 출력을 생성하는 파이프라인은 다음과 같습니다.
awk -v date="$(date --date 'yesterday' +'%m%d%Y')" '
# Count instances of IP address for finished jobs
/SUCCESS|FAILURE|TERMINATED/ {
if (m = index($0, "MACHINE:")) {
# address is after "machine"
ip = substr($0, m+9, length($0))
if (s = index(ip, " ")) {
# discard trailing text too
ip = substr(ip, 1, s-1)
}
# capture address
seen[ip]++
}
}
# Output list of addresses and counts
END {
OFS="\t"
for (ip in seen) {
print date, ENVIRON["AUTOSERVE"], seen[ip], ip
}
}
' "/tmp/log.$AUTOSERVE.dt"
적절한 날짜 일치를 통해 AUTOSERVE=NP2
샘플 데이터 파일에서 이 결과를 얻었습니다.
11162023 NP2 2 222.test.com
11162023 NP2 2 111.test.com
11162023 NP2 2 555.test.com
11162023 NP2 2 444.test.com
if (m = index($0, "MACHINE:"))
이 구조는 다음과 같다는 점에 주목할 가치가 있습니다.일그 다음에는 0이 아닌 테스트가 이어집니다. 비교를 하고 싶다면 ==
대신에 =
다음과 같이 쓸 수 있습니다.
m = index($0, "MACHINE:")
if (m<>0)
답변3
처음에 실패하는 이유는 작은따옴표를 이스케이프 처리하기 때문입니다.
perl -ne '[...] '$ARGV[1]', [...]'
따라서 귀하의 것이 $ARGV[1]
대신 쉘에 표시됩니다 perl
. 다음으로, 다음을 사용하고 있기 때문에 표준 입력에서 읽으라고 ARGV
말하고 있기 때문에 실제로 여기에 배열이 없습니다 .perl
-n
$ perl -le 'print "$ARGV[0]"' foo
foo
$ perl -nle 'print "$ARGV[0]"' foo
$
당신은 할 수누구나이를 사용 -n
하면 데이터를 파이프하거나 Perl에 자동으로 파일을 로드하고 반복하도록 요청할 수 있습니다.또는인수를 전달할 수 있지만 둘 다 전달할 수는 없습니다.
따라서 실제로 하고 싶은 일은 다음과 같습니다.
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -ne '/STATUS:\s+(\w+).+MACHINE:\s+(\w+.\w+.\w+)$/ && print join( "\t", $1, $2 ). "\n"' /tmp/log.$AUTOSERVE.dt |
grep -E '(SUCCESS|FAILURE|TERMINATED)' |
cut -f2 |
sort |
uniq -c |
perl -ne '/^\s+(\d+)\s+(.*)$/ && print join("\t", $ENV{dt}, $ENV{AUTOSERV}, $2, $1) . "\n"' > /tmp/output.txt
또는 이미 사용하고 있으므로 perl
데이터를 올바르게 추측한다고 가정합니다.
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -lne '/STATUS:\s+(SUCCESS|FAILURE|TERMINATED).+MACHINE:\s+(\w+.\w+.\w+)$/ &&
print "$2"' /tmp/log.$AUTOSERVE.dt |
sort |
uniq -c |
perl -ne '/^\s+(\d+)\s+(.*)$/ &&
print join("\t", $ENV{dt}, $ENV{AUTOSERV}, $2, $1) . "\n"' > /tmp/output.txt
심지어:
export dt=$(date -d "yesterday" '+%m%d%Y')
perl -lne '
if(/STATUS:\s+(SUCCESS|FAILURE|TERMINATED).+MACHINE:\s+(\w+.\w+.\w+)$/){
$k{$2}++;
}
END{
foreach $key (keys(%k)){
print "$ENV{dt}\t$ENV{AUTOSERV}\t$key\t$k{$key}"
}
}' > /tmp/output.txt