パスに依存しないシバン


20

2台のマシンで実行できるようにするスクリプトがあります。これらの2台のマシンは、同じgitリポジトリからスクリプトのコピーを取得します。スクリプトは、適切なインタープリターで実行する必要があります(例:)zsh

残念ながら、両方 envzshローカルおよびリモートのマシンで異なる場所に住んでいます:

リモートマシン

$ which env
/bin/env

$ which zsh
/some/long/path/to/the/right/zsh

ローカルマシン

$ which env
/usr/bin/env

$which zsh
/usr/local/bin/zsh

/path/to/script.shいつものZshようにスクリプトを実行すると使用可能なものを使用するように、どうすればシバンを設定できますPATH


8
よろしいですenv両方/ビンと/ usr / binにはないのですか?which -a env確認してみてください。
grawity

回答:


22

シバンは純粋に静的であるため、シバンを通して直接これを解決することはできません。このLCMがzshでない場合は、(シェルの観点から)シバンに「最小共通乗数」をいくつか用意し、適切なシェルでスクリプトを再実行することができます。言い換えると、すべてのシステムで見つかったシェルでスクリプトを実行し、zsh-only機能をテストし、テストがfalseであることが判明した場合は、スクリプトexecを使用zshしてテストを成功させ、そのまま続行します。

zshたとえば、のユニークな機能の1つは、$ZSH_VERSION変数の存在です。

#!/bin/sh -

[ -z "$ZSH_VERSION" ] && exec zsh - "$0" ${1+"$@"}

# zsh-specific stuff following here
echo "$ZSH_VERSION"

この単純なケースでは、スクリプトは最初に実行されます/bin/sh(80年代以降のUnixライクなシステムはすべて、BourneまたはPOSIXを理解し#!、持って/bin/shいますが、構文は両方に互換性があります)。場合$ZSH_VERSIONされていない設定、スクリプトはexec「自分自身を通じよzsh$ZSH_VERSIONが設定されている場合(または、スクリプトが既に実行されている場合zsh)、テストは単にスキップされます。ほら

zshがまったくない場合にのみ失敗$PATHします。

編集:確認execするにzshは、通常の場所でのみ、次のようなものを使用できます

for sh in /bin/zsh \
          /usr/bin/zsh \
          /usr/local/bin/zsh; do
    [ -x "$sh" ] && exec "$sh" - "$0" ${1+"$@"}
done

これは、あなたが予期していないexecものを誤って「あなたのものに入れる」ことからあなたを救う可能性が$PATHありzshます。


私は優雅さのためにこれをupvotedが、最初の場合には、原則的には、セキュリティ/互換性の問題を持っているんzshでは、$PATHあなたが期待するものではありません。
ライアンライヒ14

それに対処しようとしました。問題は、zsh標準の場所にあるバイナリが本当にであるかどうかを常に確認できるかどうかですzsh
アンドレアスヴィーゼ14

!bang行を動的にパスできます。また、zshそれがどこにあるかを自問することもできzsh -c 'whence zsh'ます。もっと簡単にできますcommand -v zsh。を動的にパスする方法については、私の回答を参照してください#!bang
mikeserv 14

1
zshバイナリ$PATHのパスを取得するためにfrom を呼び出すことは、zsh@ RyanReichが指摘した問題にまったく対処しませんか?:)
アンドレアスヴィーゼ14

あなたがexecされていない場合zsh、それ自体を、いや、私はないと思います。ただし、結果の文字列をハッシュバンに埋め込み、独自のスクリプトを実行すると、少なくとも何が得られるかがわかります。それでも、ループするよりも簡単なテストになるでしょう。
mikeserv 14

7

何年もの間、スクリプトの実行に必要なシステム上のBashのさまざまな場所を扱うために、似たようなものを使用してきました。

Bash / Zsh /など。

#!/bin/sh

# Determines which OS and then reruns this script with approp. shell interp.
LIN_BASH="/bin/sh";
SOL_BASH="/packages/utilities/bin/sun5/bash";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  $SOL_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
elif [ $OS_TYPE = "Linux" ]; then
  $LIN_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
else
  echo "UNKNOWN OS_TYPE, $OS_TYPE";
  exit 1;
fi
exit 0;

### BEGIN

...script goes here...

上記は、さまざまな通訳者に簡単に適合させることができます。重要な点は、このスクリプトが最初にBourneシェルとして実行されることです。次に、2回目の再帰呼び出しを行いますが、を### BEGIN使用してコメントの上にあるすべてのものを解析しますsed

Perl

Perlの同様のトリックを次に示します。

#!/bin/sh

LIN_PERL="/usr/bin/perl";
SOL_PERL="/packages/perl/bin/perl";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  eval 'exec $SOL_PERL -x -S $0 ${1+"$@"}';
elif [ $OS_TYPE = "Linux" ]; then
  eval 'exec $LIN_PERL -x -S $0 ${1+"$@"}';
else
  echo "$OS_TYPE: UNSUPORRTED OS/PLATFORM";
  exit 0;
fi
exit 0;

#!perl

...perl script goes here...

このメソッドは、実行するファイルが指定されたときにPerlの機能を利用して、lineの前にあるすべての行をスキップしてそのファイルを解析します#! perl


いくつかの問題:引用符の欠落、$*代わりのの"$@"使用、evalの無駄な使用、報告されない終了ステータス(exec最初のものは使用しませんでした)、-/の欠落--、stderrにないエラーメッセージ、エラー状態の終了ステータス0 、LIN_BASHに/ bin / shを使用し、不要なセミコロン(化粧)を使用し、非env変数にすべて大文字を使用します。uname -sのようなものですuname(unameはUnix名です)。スキップはの-xオプションによってトリガーされることを忘れていましたperl
ステファンシャゼル

4

注:@ jw013は、以下のコメントで次のサポートされていない異議を述べています。

一般的に、自己修正コードは悪い習慣と見なされているためです。昔の小さなアセンブリプログラムでは、条件分岐を減らしてパフォーマンスを向上させる賢い方法でしたが、今日ではセキュリティリスクが利点を上回っています。スクリプトを実行したユーザーがスクリプトに対する書き込み権限を持っていない場合、アプローチは機能しません。

私がいることを指摘することによって、彼のセキュリティの異議に答え任意の特別な許可がされている一度だけ必要なあたりにインストール/アップデートするためにアクション更新/インストール自己インストール私は個人的にかなり確保呼ぶだろう-スクリプトを。私はまたman sh、同様の手段で同様の目標を達成することへの言及を彼に指摘しました。当時、私は、セキュリティ上の欠陥や、私の答えに表されているかもしれないし、そうでないかもしれない一般的に助言されていない慣行が、それに対する答えよりも質問自体に根ざしている可能性が高いことを指摘しませんでした:

/path/to/script.shとしてスクリプトを実行すると、常にPATHで使用可能なZshが常に使用されるように、シェバンを設定するにはどうすればよいですか?

満足できなかったため、@ jw013は、まだサポートされていない議論を少なくとも2、3の誤ったステートメントでさらに進めることで異議を唱え続けました。

2つのファイルではなく、単一のファイルを使用します。[ man sh参照] パッケージには、一つのファイルが別のファイルを修正しています。自分自身を変更するファイルがあります。これら2つのケースには明確な違いがあります。入力を受け取り、出力を生成するファイルは問題ありません。実行時に自分自身を変更する実行可能ファイルは、一般的に悪い考えです。あなたが指摘した例はそれをしません。

そもそも:

ONLY EXECUTABLEいずれかにCODE EXECUTABLE SHELLスクリプトです#!自体

(ただしさえ#!ある正式に指定されていません

{   cat >|./file 
    chmod +x ./file 
    ./file
} <<-\FILE
    #!/usr/bin/sh
    {   ${l=lsof -p} $$
        echo "$l \$$" | sh
    } | grep \
        "COMMAND\|^..*sh\| [0-9]*[wru] "
#END
FILE

##OUTPUT

COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
file    8900 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
file    8900 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
file    8900 mikeserv    0r   REG   0,35      108 15496912 /tmp/zshUTTARQ (deleted)
file    8900 mikeserv    1u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv  255r   REG   0,33      108  2134129 /home/mikeserv/file
COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
sh      8906 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
sh      8906 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
sh      8906 mikeserv    0r  FIFO    0,8      0t0 15500515 pipe
sh      8906 mikeserv    1w  FIFO    0,8      0t0 15500514 pipe
sh      8906 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2

{    sed -i \
         '1c#!/home/mikeserv/file' ./file 
     ./file 
     sh -c './file ; echo'
     grep '#!' ./file
}

##OUTPUT
zsh: too many levels of symbolic links: ./file
sh: ./file: /home/mikeserv/file: bad interpreter: Too many levels of symbolic links

#!/home/mikeserv/file

シェルスクリプトは単なるテキストファイルです-何らかの効果を得るために、別の実行可能ファイルによって読み取られる必要があり、その命令はその実行可能ファイルによって解釈され、最後に実行可能ファイルが解釈を実行しますをシェルスクリプト。シェルスクリプトファイルの実行に、2つ未満のファイルを含めることはできませんzsh独自のコンパイラには例外がありますが、これについては経験がほとんどないため、ここでは説明しません。

シェルスクリプトのハッシュバン、目的のインタープリターを指すか、無関係であるとして破棄する必要があります。

シェルのトークンの認識/実行の動作は標準で定義されています

シェルには、入力の解析と解釈の2つの基本モードがあります。現在の入力が <<here_documentまたは定義している{ ( command |&&|| list ) ; } &-言い換えると、シェルはトークンを読み取った後に実行するコマンドの区切り文字として解釈しますまたはファイルを作成し、別のコマンドのファイル記述子にマップするための指示として。それでおしまい。

シェルを実行するコマンドを解釈するとき、一連のトークンを区切ります 予約語のれます。該当する場合- -かのようなトークン終値シェルが遭遇するとリストがいずれかの改行などの閉じたトークンで区切られるまでトークン開口部は、コマンドリストを読み込むし続けなければならない})ため({、実行する前に。

シェルは、単純なコマンド複合コマンドを区別します。複合コマンドは実行前に読まれなければならないコマンドのセットですが、シェルは実行されません。$expansion、その構成のいずれかに単純なコマンドが単独でそれぞれを実行するまで。

そのため、次の例では、;semicolon 予約語が個々の単純なコマンドを区切り、エスケープされていない\newline文字が2つの複合コマンドを区切ります。

{   cat >|./file
    chmod +x ./file
    ./file
} <<-\FILE
        #!/usr/bin/sh
        echo "simple command ${sc=1}" ;\
                : > $0 ;\
                echo "simple command $((sc+2))" ;\
                sh -c "./file && echo hooray"
        sh -c "./file && echo hooray"
#END
FILE

##OUTPUT

simple command 1
simple command 3
hooray

これは、ガイドラインの簡素化です。シェルビルトイン、サブシェル、現在の環境などを考慮すると、はるかに複雑になりますが、ここでの目的には十分です。

そして、ビルトインコマンドリストについて言えば a function() { declaration ; }単純なコマンドに複合コマンドを割り当てる手段にすぎませんシェルは$expansions、宣言文自体で何も実行してはいけません-含めるために<<redirections>-代わりに、定義を単一のリテラル文字列として保存し、呼び出されたときに特別なシェル組み込みとして実行する必要があります。

そのため、実行可能なシェルスクリプトで宣言されたシェル関数は、リテラル文字列形式で解釈シェルのメモリに格納されます-入力として追加されたヒアドキュメントを含めるために展開されません-ビルドされたシェルとして呼び出されるたびに、ソースファイルとは無関係に実行されますシェルの現在の環境が続く限り。

Aは<<HERE-DOCUMENTインラインファイルです

リダイレクト演算子<<<<-両方は、ヒアドキュメントとして知られるシェル入力ファイルに含まれる行のリダイレクトを許可します。にコマンドの入力に。

ここで、文書は次後に始まる単一の単語として扱われなければならない\newlineとのみを含む行があるまで継続デリミタ\newlineないと、[:blank:]の間でsは。次に、次のヒアドキュメントがあれば、開始します。形式は次のとおりです。

[n]<<word
    here-document 
delimiter

...ここで、オプションnはファイル記述子番号を表します。番号を省略すると、ヒアドキュメント標準入力(ファイル記述子0)を参照します。

for shell in dash zsh bash sh ; do sudo $shell -c '
        {   readlink /proc/self/fd/3
            cat <&3
        } 3<<-FILE
            $0

        FILE
' ; done

#OUTPUT

pipe:[16582351]
dash

/tmp/zshqs0lKX (deleted)
zsh

/tmp/sh-thd-955082504 (deleted)
bash

/tmp/sh-thd-955082612 (deleted)
sh

分かりますか?上記のシェルごとに、シェルはファイルを作成し、ファイル記述子にマップします。ではzsh, (ba)sh、シェルで通常のファイルを作成し/tmp、出力をダンプし、記述子にマップし、削除します/tmp記述子のカーネルのコピーがすべてのことが残っているようにファイルを。dashそのすべてのナンセンスを回避|pipeし、リダイレクト<<ターゲットを対象とした匿名ファイルに出力処理をドロップします。

このMAKES dashさん:

cmd <<HEREDOC
    $(cmd)
HEREDOC

機能的にのと同等bash

cmd <(cmd)

一方、dashの実装は少なくともPOSIXlyに移植可能です。

WHICH MAKES いくつかのファイルを

だから私が行うとき、以下の答えで:

{    cat >|./file
     chmod +x ./file
     ./file
} <<\FILE
#!/usr/bin/sh
_fn() { printf '#!' ; command -v zsh ; cat 
} <<SCRIPT >$0
    [SCRIPT BODY]
SCRIPT    

_fn ; exec $0
FILE

次のことが起こります。

  1. まずcat、シェルがfor FILEに作成したファイルの内容を./file実行し、実行可能にしてから実行します。

  2. カーネルはを解釈し、に割り当てられたファイル記述子で#!呼び出します/usr/bin/sh<read ./file

  3. shは、で始まり、で終わる複合コマンドで構成されるメモリに文字列をマップします。_fn()SCRIPT

  4. とき_fnに呼び出され、shまず記述子にして定義されたファイルをマップ解釈する必要があります<<SCRIPT...SCRIPT 前に呼び出す_fnので、内蔵のユーティリティ特別なようSCRIPTです_fnさん<input.

  5. 文字列によって出力printfcommandに書き出される_fn標準アウト >&1 -現在のシェルのリダイレクトされますARGV0-か$0

  6. catその連結<&0 標準入力 -ファイル・ディスクリプタをSCRIPTかけ- >切り捨て現在のシェルのARGV0引数、または$0

  7. そのすでに現在で読み込み完了複合コマンドsh execおよび新しく書き換え- -実行可能だ$0引数を。

時間から./fileその含まれる命令は、それがする必要があることを指定するまで呼ばれてexec再びD、sh単一でそれを読み込み、複合コマンドながら、それはそれらを実行して一度./file自体はまったく何もしない喜んでその新しい内容を受け入れ除きます。実際に作業しているファイルは/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.

おかげで、すべての後

したがって、@ jw013が以下を指定する場合:

入力を受け取り、出力を生成するファイルは問題ありません...

...この答えに対する誤った批判の中で、彼は実際にここで使用されている唯一の方法を無意識のうちに容認しています。

cat <new_file >old_file

回答

ここでの答えはすべて良いのですが、完全に正しいものはありません。誰もが動的かつ恒久的にパスできないと主張しているようです#!bang。以下に、パスに依存しないシバンのセットアップのデモを示します。

デモ

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me ; exec $0
FILE

出力

        $0    : ./file
        lines : 13
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
12 >    SCRIPT
13 >    _rewrite_me ; out=$0 _rewrite_me ; exec $0

        $0    : /home/mikeserv/file
        lines : 8
        !bang : #!/usr/bin/zsh
        shell : /usr/bin/zsh

1 >     #!/usr/bin/zsh
2 >             printf "
...
7 >             sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
8 >                     sed -e 'N;s/\n/ >\t/' -e 4a\\...

分かりますか?スクリプトに自分自身を上書きさせるだけです。そして、git同期後に一度だけ発生します。その時点から、#!bang行に正しいパスがあります。

今ではそのほとんどすべてが毛羽立っています。これを安全に行うには、次のものが必要です。

  1. 上部で定義され、書き込みを行う下部で呼び出される関数。このようにして、必要なものをすべてメモリに保存し、上書きを開始する前にファイル全体が読み込まれるようにします。

  2. パスがどうあるべきかを決定する何らかの方法。command -vそれにはかなり良いです。

  3. ヒアドキュメントは実際のファイルなので、本当に役立ちます。その間、スクリプトを保存します。文字列も使用できますが...

  4. 実行するものと同じコマンドリスト内のスクリプトを上書きするコマンドをシェルが読み取ることを確認する必要があります。

見て:

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me
exec $0
FILE

execコマンドを1行だけ下に移動したことに注意してください。今:

#OUTPUT
        $0    : ./file
        lines : 14
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
13 >    _rewrite_me ; out=$0 _rewrite_me
14 >    exec $0

スクリプトは次のコマンドで読み取ることができないため、出力の後半は取得しません。それでも、欠落しているコマンドは最後のものだけだったため:

cat ./file

#!/usr/bin/zsh
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...

このスクリプトは、主にすべてヒアドキュメントに記載されていたため、必要な通りに実行されましたが、適切に計画しない場合は、ファイルストリームを切り捨てることができます。


一般的に、自己修正コードは悪い習慣と見なされているためです。昔の小さなアセンブリプログラムの時代には、条件分岐を減らしてパフォーマンスを向上させる賢い方法でしたが、今日ではセキュリティリスクが利点を上回っています。スクリプトを実行したユーザーがスクリプトに対する書き込み権限を持っていない場合、アプローチは機能しません。
jw013 14

@ jw013明らかに、スクリプトインストールまたは更新しようとするがスクリプトインストールまたは更新する権限を持っていない場合、実行可能スクリプトインストールまたは更新する私のアプローチは機能しません実際、それが特にこの回答を他のすべての回答よりも優れている理由です-必要に応じて正確な#!bang行を提供でき、インストール中に最初の呼び出しで行うために特別な権限のみが必要です。そして、繰り返しますが、コード自己修正するのは悪い習慣であると単純に言いませんman command。矛盾した意見をご覧ください。
mikeserv 14

man command矛盾した意見を参照してください -見つけられない。あなたが話していた特定のセクション/段落に私を導くことができますか?
jw013 14

@ jw013-私の間違い、それはman sh- 「command -v」を検索してくださいman先日見ていたページの1つにあることがわかりました。
mikeserv 14

私は仮定し、これはあるcommand -vあなたがから話していた例man sh。これは通常のインストーラスクリプトであり、自己修正型ではありません。自己完結型のインストーラーでさえ、変更前の入力のみを含み、変更を他の場所に出力します。彼らはあなたが推奨している方法で自分自身を書き換えません。
jw013

1

以下に、シバンを修正する自己修正スクリプトを作成する1つの方法を示します。このコードは、実際のスクリプトの先頭に追加する必要があります。

#!/bin/sh
# unpatched

PATH=`PATH=/bin:/usr/bin:$PATH getconf PATH`
if [ "`awk 'NR==2 {print $2;exit;}' $0`" = unpatched ]; then
  [ -z "`PATH=\`getconf PATH\`:/usr/local/bin:/some/long/path/to/the/right:$PATH command -v zsh`" ] && { echo "zsh not found"; exit 1; }
  cp -- "$0" "$0.org" || exit 1
  mv -- "$0" "$0.old" || exit 1
  (
    echo "#!`PATH=\`getconf PATH\`:$PATH command -v zsh`" 
    sed -n '/^##/,$p' $0.old
  ) > $0 || exit
  chmod +x $0
  rm $0.old
  sync
  exit
fi
## Original script starts here

コメント:

  • スクリプトが存在するディレクトリでファイルを作成および削除する権限を持つ人が1回実行する必要があります。

  • 一般的な信念にもかかわらず/bin/sh、POSIX準拠のOSであってもPOSIXシェルであることが保証されていないため、レガシーbourneシェル構文のみを使用します。

  • PATHをPOSIX準拠のパスに設定し、その後に「偽の」zshを選択しないように、可能なzshの場所のリストを設定します。

  • 何らかの理由で自己修正スクリプトが歓迎されない場合、1つではなく2つのスクリプトを配布するのは簡単です。1つ目はパッチを適用したいスクリプトで、2つ目は前者を処理するために少し修正することをお勧めします。


/bin/shポイントは、良いものです-しかし、その場合には、あなたはpremodified必要です#!すべてで?そして、ではありませんawk同じように可能性が高いと偽のようzshですか?
mikeserv

@mikeserv回答が更新され、POSIX awkを呼び出しました。事前に変更されたシバンは、ログインシェルである場合に、非Bourne互換シェルによってスクリプトが解釈されるのを防ぐためにあります。
jlliagre

理にかなっています。それが機能し、本にこだわっており、可能なシェル環境/ファイル処理、特に使用しているバックアップファイルの健全な理解を実証しているので、私はそれを支持sed -iしました。私は個人的に考えて$PATH、安全に、私はいくつかの行にここに把握できるよう、より良い簡単かつ明示的依存性および/または厳格かつ明示的なテストを定義することで処理されるような問題は別の答えにコメントで指摘しているあなたの住所-例えば、今getconfかもしれません偽物ですが、可能性はほとんどありません。同じように、zshそしてawk.
mikeserv

@mikeserv、偽のgetconfを呼び出すリスクを減らすためにスクリプトを修正。
jlliagre

$(getconf PATH)ボーンではありません。cp $0 $0.oldzsh構文です。ボーンと同等のでしょうcp "$0" "$0.old"、あなたがしたいと思うけれどもcp -- "$0" "$0.old"
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.