引用符はフィールドを区別しません。

引用符はフィールドを区別しません。

現在、次の種類のテキストに対して操作を実行したい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履歴拡張の影響を受けない一重引用符文字。

フィールド分割は、引用符のない文字でのみ発生します。ここにはそんなことは全くありません。1つのフィールド全体、これすべて議論ですsedコマンドに。

これを行うと、現在実行中のタスクでTAB文字が多すぎていることがわかります。 ☺

手をつないで...

sed作業に適したツールではありません。awk現在削除されている2番目の答えのように、実際にはそうではありません。長期的に現在の作業が少ない場合は、適切なYAMLパーサー(Pythonのyamlモジュールなど)を使用してYAMLを解析することをお勧めします。

このはい作業に適したツールを含む簡単な1行。たとえば、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保存して終了するという意味です。

すべてが1つの包装で包まれています。カン「ここ、博士」


コメントで指摘したように、元の目的は静的ユーザーをハードコードするのではなく、そのユーザーのリストを繰り返すことでした。

これを行うには、テキストファイルに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の仕事ではありません。 )

関連情報