따옴표는 필드를 구분하지 않습니다.

따옴표는 필드를 구분하지 않습니다.

현재 다음 유형의 텍스트에 대해 작업을 수행하려는 sed 명령이 있습니다.

user:
        ensure: 'present'
        uid: '666'
        gid: '100'
        home: '/home/example'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: ''

다음 명령을 사용하여 원하는 유형의 결과를 얻을 수 있습니다.

sed '/사용자:/!b;n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호: \x27\!\!\x27/g'

user:
        ensure: 'present'
        uid: '666'
        gid: '100'
        home: '/home/example'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '!!'

이 명령의 문제점은 user:정적이라는 것입니다. 사용자 목록을 반복하고 sed 명령에서 특정 사용자 대신 bash 변수를 사용할 수 있기를 원합니다. 하지만 이렇게 하려면 작은 따옴표 대신 큰 따옴표를 사용해야 합니다. 그러나 이 명령을 사용하면 다음과 같습니다.

sed "/user:/!b;n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호: \x27\!\!\x27/g"

!b내가 사용하는 sed 명령 에서 "!"에 대해 불평합니다 . (bash가 그것을 해석하려고 하기 때문에) 그러나 다음과 같이 이스케이프하면:

sed "/user:/\!b;n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호: \x27\!\!\x27/g"

그러면 다음 오류가 발생합니다.

sed: -e 표현식 #1, 문자 6: 알 수 없는 명령: `\'

어떻게 작동하게 할 수 있나요?

답변1

따옴표는 필드를 구분하지 않습니다.

이는 쉘 언어 구문에서 중요하지만 종종 잊혀지는 측면입니다. "주장 인용" 사고 모델은 실제로 단순하고 잘못된 것입니다. 인용할 것은 인용하고, 인용할 것은 인용하라부분결국 하나의 주장이 됩니다.

이것이 바로 당신이 여기서 해야 할 일입니다. 아이러니하게도 현재 삭제된 첫 번째 답변은 다음과 같습니다.아주 근접한.

sed실행에 대한 실제 단일 인수로 제공하려는 최종 문자열은 다음과 같습니다.

/user:/!b;n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호: \x27!!\x27/g
user부분은 실제 사용자 이름에 따라 변경됩니다. 하지만 여기까지 오려면원하지 않는다전체 매개변수에는 단일 인용 체계가 필요합니다. 각 부분에서 논쟁을 만들 수 있습니다.

매개변수 확장이 필요한 부분에는 큰따옴표를 사용하고 히스토리 확장이 필요한 부분에는 작은따옴표를 사용합니다.아니요발생하다:

-ri를 읽을 때
하다
   sed -e '/'"$i"':/!b;n;n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호: \x27!! \ x27 /g' puppet-users.yaml > puppet-users{new}.yaml
   mv puppet-users{new}.yaml puppet-users.yaml
<user_list.txt를 완료하세요.

이것은:

  1. 작은따옴표 문자 /.
  2. 큰따옴표 문자는 $i매개변수 확장(물론 다른 모든 확장 및 대체도 포함)의 영향을 받습니다.
  3. :/!b;n;n;n;n;n;n;n;n;n;s/.*/\t\tpassword: \x27!!\x27/g기록 확장의 영향을 받지 않는 작은따옴표 문자입니다.

필드 분할은 인용되지 않은 문자에서만 발생합니다. 여기에는 그런 것이 전혀 없습니다.하나의 필드 전체, 이는모두 논쟁이다sed명령 에 .

이 작업을 수행하고 나면 현재 수행 중인 작업에서 너무 많은 TAB 문자를 사용하고 있음을 알게 될 것입니다. ☺

손을 잡고...

sed작업에 적합한 도구는 아닙니다. awk현재 삭제된 두 번째 답변에서와 같이 실제로는 그렇지 않습니다 . 장기적으로 현재 작업이 덜 사소해지면 적절한 YAML 파서(예: Python의 yaml 모듈)를 사용하여 YAML을 구문 분석하는 것이 더 좋습니다.

작업에 적합한 도구가 포함된 간단한 한 줄입니다. 예를 들어 yq사용법이 다음과 같은 다양한 도구입니다(아쉽게도 모두 다르기 때문입니다).

-ri를 읽을 때
하다
   yq < puppet-users.yaml "$i".password="'\!\!'" > puppet-users{new}.yaml
   mv puppet-users{new}.yaml puppet-users.yaml
<user_list.txt를 완료하세요.

추가 읽기

답변2

댓글로 도움을 주신 @steeldriver에게 감사드립니다. 마침내 명령을 다음과 같이 리팩터링하여 문제를 해결했습니다.

sed "/사용자:/{n;n;n;n;n;n;n;n;n;s/.*/\t\t비밀번호:\x27\!\!\x27/g}"

user:그런 다음 bash 스크립트 변수 대체를 사용할 수 있습니다 .

답변3

나는 당신에게 사용을 제안 할 수 있습니다ex, 선행 작업 및 비시각적 형식vi, 다양한 주소 지정 요구 사항을 충족하려면:

ex file.txt <<'EOF'
0/^user://password:/s/:.*/: '!!'/
0/^something else://subline to change:/s/:.*/: new value/
x
EOF

0라인 0을 나타냅니다. 즉, 파일의 시작 부분에서 검색이 시작됩니다.

/^user:/주소입니다. ex지정할 수 있다는 점을 제외하면 Sed 또는 Awk 주소처럼 작동합니다.추가의주소는 첫 번째 주소에 지정된 줄에서 가져옵니다. (역방향 주소 지정을 사용할 수도 있습니다.)

/password:/다른주소. 따라서 password:패턴이 발견된 지점부터 앞으로 패턴을 검색하여 찾은 라인을 지정합니다.^user:

s/old/new/명령은 Sed와 동일하게 작동합니다.

x저장하고 종료한다는 의미입니다.

모든 것이 하나의 포장으로 포장되어 있습니다.쿵 "여기요, 박사님."


의견에서 지적했듯이 원래 목적은 정적 사용자를 하드코딩하는 것이 아니라 해당 사용자 목록을 반복하는 것이었습니다.

이렇게 하려면 텍스트 파일에 userlist.txt다음이 포함되어 있다고 가정합니다.

user1
user2

또한 각 사용자에 대해 동일한 교체를 수행한다고 가정하면(예: "password" 필드를 로 설정 '!!') sed를 사용하여 사용자 목록을 ex명령으로 변환하면 됩니다.

!!대화형 셸에서 확장할 예정 이므로 대화형으로 사용하려면 먼저 다음을 실행하세요.

set +H

그 다음에:

< userlist.txt sed -e "s_.*_0/^&://password:/s/:.*/: '!!'/_" -e $'$a\\\nx' | ex file.txt

파이처럼 쉽습니다. ;)


데모:

$ cat file.txt 
user1:
        ensure: 'present'
        uid: '666'
        gid: '100'
        home: '/home/example1'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '1'
user2:
        ensure: 'present'
        uid: '667'
        gid: '100'
        home: '/home/example2'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '2'
user3:
        ensure: 'present'
        uid: '668'
        gid: '100'
        home: '/home/example3'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '3'
user4:
        ensure: 'present'
        uid: '669'
        gid: '100'
        home: '/home/example4'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '4'
$ cat userlist.txt 
user3
user2
$ set +H
$ < userlist.txt sed -e "s_.*_0/^&://password:/s/:.*/: '!!'/_" -e $'$a\\\nx' | ex file.txt
$ cat file.txt 
user1:
        ensure: 'present'
        uid: '666'
        gid: '100'
        home: '/home/example1'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '1'
user2:
        ensure: 'present'
        uid: '667'
        gid: '100'
        home: '/home/example2'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '!!'
user3:
        ensure: 'present'
        uid: '668'
        gid: '100'
        home: '/home/example3'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '!!'
user4:
        ensure: 'present'
        uid: '669'
        gid: '100'
        home: '/home/example4'
        comment: ''
        password_max_age: '99999'
        password_min_age: '0'
        shell: '/bin/false'
        password: '4'
$ 

바라보다.

답변4

!역사 확장 캐릭터입니다. 기록 확장은 대화식으로 실행될 때 bash(및 일부 다른 셸)의 기능입니다. (명시적으로 활성화하지 않는 한) 스크립트에서는 특별한 의미가 없습니다.

!큰따옴표 안에 역사적 확장 의미를 유지하세요. 그러나 큰따옴표 안에는 \!백슬래시를 의미합니다. 따라서 큰따옴표 안에 느낌표를 포함할 수 없습니다. 다른 형식의 인용( \!따옴표 외부 또는 !작은따옴표 내부) 을 사용해야 합니다 . (또 다른 해결책은 !변수에 넣는 것입니다 .)

예를 들어, 같은 단어에 인용 형식을 혼합하는 것을 막을 수는 없습니다.

sed "/$username:/"\!"b;n;n;n;n;n;n;n;n;n;s/.*/\t\tpassword: '"\!\!"'/g"

(하지만 동의해야 해제이드 BP, 이것은 sed의 작업이 아닙니다. )

관련 정보