リモートシェルでローカルシェルの変数を参照したい


ローカルで定義、初期化した変数を使ってリモートシェル上でsedコマンドを使いたいと考えております。  
例を示すと

[code]

    local.sh
    #!/bin/bash
    string="hello"
    ssh host  'sed -i -e s/world/${string}/g /home/user/test.txt'
    
[/code]

このスクリプトを実行した際にリモート上のtest.txtでworld -> hello の置換が実行されることが理想です。  
この時どのように変数を指定すればよろしいでしょうか？




fumiyas さんもおっしゃっていますが、 sed と ssh
組み合わせてやろうとすると、いくつか制御文字が使われた場合などで、バグが発生しています。まず具体例から。

質問者さんがやりたいことは、 world -> hello であるならば、ひとまず以下で達成できます。

[code]

    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    FROM_STRING=world
    TO_STRING=hello
    ssh "$SSH_TO" sed -e "s/${FROM_STRING}/${TO_STRING}/g" $TARGET_FILE
    
[/code]

しかしこれは、変数に sed の制御文字が入ってきた時点で正しく動作しなくなってしまいます。

[code]

    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    FROM_STRING=world
    TO_STRING=hel/lo
    ssh "$SSH_TO" sed -e "s/${FROM_STRING}/${TO_STRING}/g" $TARGET_FILE
    # sed: -e expression #1, char 13: unknown option to `s'
    
[/code]

これを解消するためには、 sed ではなく awk を用います。awk だと環境変数から直接文字列を参照できます。また、 ssh
は引数をスペースでつなげて文字列として対象ホストで実行します。これを正しく取り扱うために、環境変数のセットコマンドにはエスケープを施します。これをスクリプトにすると次。

[code]

    #!/bin/sh
    
    awk_script() {
        cat <<"EOF"
    { gsub(ENVIRON["FROM_STRING"], ENVIRON["TO_STRING"]); print }
    EOF
    }
    
    # 1: var_name
    # 2: content
    ssh_set_variable() {
        cat <<EOF
    ${1}='$(printf '%s\n' "$2" | sed -e "s/'/\\'/g")'
    EOF
    }
    
    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    TO_STRING='any string should be allowed...'
    FROM_STRING=world  # any should be OK also
    ssh "$SSH_TO" \
        "$(ssh_set_variable FROM_STRING "${FROM_STRING}") " \
        "$(ssh_set_variable TO_STRING "${TO_STRING}") " \
        awk \'"$(awk_script)"\' \'"${TARGET_FILE}"\'
    
[/code]

スクリプトの解説を書こうと思いましたが、このスクリプトを書くので力尽きてしまったので、ひとまず動くものを共有しています。。




fumiyas さんもおっしゃっていますが、 sed と ssh
組み合わせてやろうとすると、いくつか制御文字が使われた場合などで、バグが発生しています。まず具体例から。

質問者さんがやりたいことは、 world -> hello であるならば、ひとまず以下で達成できます。

[code]

    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    FROM_STRING=world
    TO_STRING=hello
    ssh "$SSH_TO" sed -e "s/${FROM_STRING}/${TO_STRING}/g" $TARGET_FILE
    
[/code]

しかしこれは、変数に sed の制御文字が入ってきた時点で正しく動作しなくなってしまいます。

[code]

    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    FROM_STRING=world
    TO_STRING=hel/lo
    ssh "$SSH_TO" sed -e "s/${FROM_STRING}/${TO_STRING}/g" $TARGET_FILE
    # sed: -e expression #1, char 13: unknown option to `s'
    
[/code]

これを解消するためには、 sed ではなく awk を用います。awk だと環境変数から直接文字列を参照できます。また、 ssh
は引数をスペースでつなげて文字列として対象ホストで実行します。これを正しく取り扱うために、環境変数のセットコマンドにはエスケープを施します。これをスクリプトにすると次。

[code]

    #!/bin/sh
    
    awk_script() {
        cat <<"EOF"
    { gsub(ENVIRON["FROM_STRING"], ENVIRON["TO_STRING"]); print }
    EOF
    }
    
    # 1: var_name
    # 2: content
    ssh_set_variable() {
        cat <<EOF
    ${1}='$(printf '%s\n' "$2" | sed -e "s/'/\\'/g")'
    EOF
    }
    
    : ${SSH_TO:=host}
    TARGET_FILE=/home/ec2-user/test.txt
    TO_STRING='any string should be allowed...'
    FROM_STRING=world  # any should be OK also
    ssh "$SSH_TO" \
        "$(ssh_set_variable FROM_STRING "${FROM_STRING}") " \
        "$(ssh_set_variable TO_STRING "${TO_STRING}") " \
        awk \'"$(awk_script)"\' \'"${TARGET_FILE}"\'
    
[/code]

スクリプトの解説を書こうと思いましたが、このスクリプトを書くので力尽きてしまったので、ひとまず動くものを共有しています。。

