目的
mv
(または同様の性格のコマンド)を使用するたびに、シェルは "こんにちは、mvを使用しましたか?申し訳ありません。 mv -iを使ってやり直してください。この相互作用を実行する合理的な方法を知りたいです。
目的は、コンピュータを使用するたびに苦情を表示してmv
再入力する必要があることですmv -i
。そのように、私は入る筋肉の記憶を発達させますmv -i
。
私のコンピュータが自分のコンピュータを内部mv
に変換できるようにすることはmv -i
いいえ目的を完全に崩します。
これが愚かな考えのように聞こえたら、その理由とより良い解決策を知りたいです。
背景
mv
過去にファイルを使用して誤って上書きしたことがあります。しかし、この意見では、mv
実際に動作するようにエイリアスを使用することを提案します。mv -i
、私は同意するそのようなカスタマイズの結果についての意見。を入力するのに慣れるのではなく、他のコンピュータで間違えないようにmv
入力する習慣を聞く方が良いです。mv -i
心配する
そのような修正が呼び出しに影響を与えるかどうか心配されます。中断してEnterキーを押して直接実行したときにのみプロンプトを表示するmv
賢い方法はありますか?mv
ノート
- ファイルをバックアップしましたが、関係ありません。
- 私は意識を高めるために机に付箋を貼るか、壁にポスターを貼ることができることを知っています。
- 環境: Debian、XFCE、xfce4-terminal、Bash
答え1
私はラッパー関数を使用します。これを.bashrcに追加できます(テストされていません)。
mv () {
case $1 in
-*i*) # ok, used `mv -i ...`: invoke the mv command, passing all args
command mv "$@"
;;
*)
echo "hey, use 'mv -i'" >&2
false
;;
esac
}
答え2
しかし、@glennJackmanのアプローチ-
最初の引数が次から始まり、含まれていることを確認するラッパーを使用すると、ほとんどの一般的なケースでは十分ですが、場合によってはi
失敗します。
- 邪魔します
mv --help
(--version
含まれていません-i
)。 -i
では検出できません(またはmv -v -i a b
環境にisがない場合、mv a b -i
GNUは許可します)。mv
POSIXLY_CORRECT
- GNUは含まれていません
mv --interactive
//mv --in
...mv --i
-i
欠けているものを見逃しmv --no-target-dir a b
たりmv -Tdir file
...
ラッパーがこれらすべてのケースをカバーするには、同じ方法でオプションを解決する必要がありますmv
。両方の実装では、同じ方法でオプションを解析しません。mv
同じ実装バージョン間にも違いがあります。
GNU実装mv
(およびほとんどのユーティリティ)がそれを解析するために使用するオプションですgetopt_long()
。
同じ引数を使用してラッパーをgetopt_long()
呼び出せる場合は、ソートされます。これは2つの質問を残します。
getopt_long()
シェルでインターフェースを見つける必要がありますmv
私たちはどちらが伝わるのかを知る必要があります。getopt_long()
GNU/Linux システムを使用する場合は、いくつかの可能な方法があります。
getopt_long()
GNUツールボックスにはシェルCLIはありませんがutil-linux
、:と一緒に使用するgetopt
ユーティリティがあります。-o
-l
GNU / Linuxでは、GNUユーティリティはgetopt_long()
libcでこの関数を呼び出します。これはGNU libcであるため、トレースltrace
ライブラリの呼び出しを使用して解析オプションの呼び出しを確認できますgetopt_long()
。mv
$ ltrace -e getopt_long mv -:
mv->getopt_long(2, 0x7ffcf7febd68, "bfint:uvS:TZ", 0x5650dd02fb20, nilmv: invalid option -- ':'
) = 63
Try 'mv --help' for more information.
+++ exited (status 1) +++
(-:
保証は無効なオプションの場合)
私たちは単純なオプションだけを見たので十分ではありません。しかし、ltrace
そこからパラメータをデコードするように設定することも、long_options
私たちが気にしないパラメータを隠すように設定することもできます。
概念証明として、以下は、sh
呼び出し前に-i
/ --interactive
(または--help
/ --version
)またはその略語を確認するラッパー関数の互換コードを出力するzshスクリプトですmv
。
#! /bin/zsh -
set -o extendedglob
die() {
print -ru2 -- "$@"
exit 1
}
for cmd do
getopt_long_call=$(
ltrace -F/dev/fd/3 3<<'EOF' -o/dev/fd/4 4>&1 > /dev/null 2>&1 -s 999 -A999 -e getopt_long "$cmd" -:
int getopt_long(hide(int),hide(addr),string,array(struct(string,int,hide(int*),hide(int)),zero),hide(int*));
EOF
)
getopt_long_call=${getopt_long_call%%$'\n'*}
[[ $getopt_long_call = (#b)[^\"]#'getopt_long("'([^\"]#)'", [ '(*)' ]) = '<-> ]] ||
die "Can't determine what getopt_long call $cmd does"
short_opts=() long_opts=()
: ${match[1]//(#m)?:#/${short_opts[1+$#short_opts]::=$MATCH}}
: ${match[2]//(#b)'{ "'([^\"]#)'", '(<->)' }'/${long_opts[1+$#long_opts]::=$match[1]${(l($match[2])(:))}}}
opts_with_args=(-${(M)^short_opts:#*:} --${(M)^long_opts:#*:})
opts_with_args=(${opts_with_args%%:#})
print -r -- $cmd'() {
(
opt=$(getopt -qo '${(j[]qq)short_opts}' '${(qq)long_opts/#/-l}' -- "$@") || exit 0
eval "set -- $opt"
while [ "$#" -gt 0 ]; do
case $1 in
(-i | --interactive | --version | --help) exit;;
(--) printf >&2 "%s\n" "Please run '$cmd' with -i/--interactive"
exit 1;;
('${(j[ | ])${(qq)opts_with_args}}') shift;;
esac
shift
done
echo >&2 "Oops. Something when wrong"
exit 1
) || return
command '$cmd' "$@"
}'
done
たとえば、私のシステムでは、that-script mv rm
出力は次のようになります。
mv() {
(
opt=$(getopt -qo 'bfint:uvS:TZ' '-lbackup::' '-lcontext' '-lforce' '-linteractive' '-lno-clobber' '-lno-target-directory' '-lstrip-trailing-slashes' '-lsuffix:' '-ltarget-directory:' '-lupdate' '-lverbose' '-lhelp' '-lversion' -- "$@") || exit 0
eval "set -- $opt"
while [ "$#" -gt 0 ]; do
case $1 in
(-i | --interactive | --version | --help) exit;;
(--) printf >&2 "%s\n" "Please run mv with -i/--interactive"
exit 1;;
('-t' | '-S' | '--backup' | '--suffix' | '--target-directory') shift;;
esac
shift
done
echo >&2 "Oops. Something when wrong"
exit 1
) || return
command mv "$@"
}
rm() {
(
opt=$(getopt -qo 'dfirvIR' '-lforce' '-linteractive::' '-lone-file-system' '-lno-preserve-root' '-lpreserve-root::' '-l-presume-input-tty' '-lrecursive' '-ldir' '-lverbose' '-lhelp' '-lversion' -- "$@") || exit 0
eval "set -- $opt"
while [ "$#" -gt 0 ]; do
case $1 in
(-i | --interactive | --version | --help) exit;;
(--) printf >&2 "%s\n" "Please run rm with -i/--interactive"
exit 1;;
('--interactive' | '--preserve-root') shift;;
esac
shift
done
echo >&2 "Oops. Something when wrong"
exit 1
) || return
command rm "$@"
}
あなたはこれを行います:
eval "$(that-script mv rm)"
シェルの対話型モード設定(~/.zshrc
、~/.bashrc
...)。
対応するラッパーを定義します。それから:
$ rm a -i
rm: remove regular file 'a'? n
$ rm a --int
rm: remove regular file 'a'? n
$ mv --version
mv (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Mike Parker, David MacKenzie, and Jim Meyering.
$ POSIXLY_CORRECT=1 rm a -i
Please run rm with -i/--interactive
答え3
最も簡単な方法はエイリアスを作成することです。
alias mv="mv -i"
より複雑ですが、よりカスタマイズ可能なアプローチは、mv
シェルスクリプトを作成して配置し、/bin
実際に別の「秘密」ディレクトリmv
に移動することです。/sbin
代替スクリプトは警告を発行し、フルパスをmv
使用して元のスクリプトを呼び出します。
mv
ツールの名前を変更したり、ツールを完全に削除したくない場合。
はい、これらの修正により他のスクリプトが失敗する可能性があります。その場合は、これらのスクリプトに移動して交換して修正できますmv
。/secret/path/mv
別の方法は、日常生活のための特別なユーザーを作成することです。危険なツールを除いて、実際の$HOME/bin
コピーを作成します(通常は)。日常のユーザーにPATHを設定すると、代わりに/bin
使用されます。一部のアプリケーションに「危険な」ツールが必要な場合(そのアプリケーションは「日常的な」ユーザーには適していません)、より多くの権限を持つユーザーに切り替えます。/home/everyday/bin
/bin