예상을 사용하여 정규식을 사용하여 "$"로 끝나는 항목을 일치시키려면 어떻게 해야 합니까?

예상을 사용하여 정규식을 사용하여 "$"로 끝나는 항목을 일치시키려면 어떻게 해야 합니까?

를 사용하여 원격 서버에 일부 명령을 실행하고 싶습니다 expect. 그런데 사용할 때 .*\$$명령을 보내라는 원격 프롬프트를 항상 일치시킬 수는 없습니다.

아래는 내 스크립트입니다.

#!/bin/ksh
while read -r line; do
/usr/linux/bin/expect -c "
set timeout 20
spawn ssh padmin@$line
expect {
    -re \".*(P|p)assword:\" {send \"$2\r\"}
}
expect {
    -re \".*\$$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r\"}
}
expect {
    -re \".*\$$\" {exit 0}
    eof {exit 0}
}
send_user \"$expect_out(1,string)\n\"
" 2>"$line".errlog &
done <"$1"

디버그 모드를 사용하여 확인할 때 다음 메시지가 나타납니다.

expect version 5.45.4
spawn ssh [email protected]
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3474502}
Gate keeper glob pattern for '(P|p)assword:' is ''. Not usable, disabling the performance booster.

expect: does "" (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=no
[email protected]'s password: 
expect: does "[email protected]'s password: " (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) "password:"
expect: set expect_out(1,string) "p"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "[email protected]'s password:"
send: sending "padmin\r" to { exp4 }
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no


expect: does " \r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no
Last unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5
Last login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4
Welcome!
/etc/profile[63]: hostname:  not found.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
:/home/padmin$ 
expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
expect: timed out
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
sighandler: handling signal(2)
async event handler: Tcl_Eval(exit 130)

그렇다면 이 문제를 어떻게 해결해야 할까요? 감사합니다.

답변1

/usr/linux/bin/expect -c ...;에 전달된 문자열을 살펴보겠습니다. 명령은 실제로 무엇을 수신합니까? 명령을 다음으로 바꾸면 echo예상되는 출력을 볼 수 있습니다.

expect {
    -re ".*$" {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"}
}

이제 에 전달된 문자열을 사용하여 동일한 작업을 수행해 보겠습니다 send. 이번에는 이를 문자열을 구문 분석하는 데 사용해야 하므로 이를 Expect 명령으로 expect바꿉니다 . 예를 들면 다음과 같습니다.sendsend_user

expect <<\!
send_user "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"
!

예상되는 오류가 발생합니다.

can't read "9": no such variable

$9따라서 를 사용하여 보간을 중지 해야 합니다 \$9.

원래 ksh 스크립트로 돌아가서 \큰따옴표로 묶인 문자열 내에 작은따옴표를 생성하려면 를 사용해야 합니다 \\. 따라서 결국 해결책은 다음과 같습니다.

expect {
    -re \".*$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \\\$9}'\r\"}
}

작은따옴표와 큰따옴표 사이에 공백이 없으면 교대로 사용하여 이러한 문제를 줄일 수 있습니다. 예: echo "hi"'there'생산 hithereecho '"who'"'s"'"'생산 "who's". 여기서 문서의 사용 및 스타일은 <<!또 다른 수준의 인용을 제공합니다.<<\!

답변2

예상 코드를 쉘 스크립트에 혼합할 때 heredoc을 사용하는 것이 가장 좋습니다. 그러면 최소한 한 단계의 인용 지옥을 피할 수 있습니다. Tcl/expect에서 정규 표현식은 따옴표보다는 중괄호로 묶는 것이 가장 좋습니다. 그러면 백슬래시를 더 쉽게 처리할 수 있습니다.

/usr/linux/bin/expect <<END_EXPECT 2>"$line".errlog &
    set timeout 20
    spawn ssh padmin@$line
    expect {
        -re {.*(P|p)assword:} {send "$2\r"}
    }
    expect {
        -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
    }
    expect {
        -re {.*\$$} {exit 0}
        eof {exit 0}
    }
    send_user "$expect_out(1,string)\n"
END_EXPECT

줄 바꿈과 캐리지 리턴을 작성해야 할지 잘 모르겠습니다 \r. \\r시도해 볼 수 있습니다.

환경을 통해 예상되는 쉘 변수를 전달할 수 있으며 전체 heredoc을 인용할 수 있습니다.

host="$line" passwd="$2" /usr/linux/bin/expect <<'END_EXPECT' 2>"$line".errlog &
    # ...........................................^..........^
    set timeout 20
    spawn ssh padmin@$env(host)
    expect {
        -re {.*(P|p)assword:} {send "$env(passwd)\r"}
    }
    expect {
        -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
    }
    expect {
        -re {.*\$$} {exit 0}
        eof {exit 0}
    }
    send_user "$expect_out(1,string)\n"
END_EXPECT

이 줄의 목적은 무엇입니까 send_user? 예상 모드에서는 아무 것도 캡처하지 않고 실제로 expect마지막 명령에서 예상을 종료합니다. 거기에서 무엇을 볼 수 있을 것 같나요?

답변3

문제를 발견했습니다. 문제는 $상징이다. \\\$예상 출력을 사용하면 스크립트가 제대로 작동합니다. $정규식에서도 특수 기호이기 때문입니다 . 정규식을 사용할 때는 \$정규식 끝 기호로 표시됩니다. 그래서 우리는 \\\$그것을 사용하여 변환 해야 합니다 \$. 감사합니다.

관련 정보