目的

目的

目的

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 -iGNUは許可します)。mvPOSIXLY_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つの質問を残します。

  1. getopt_long()シェルでインターフェースを見つける必要があります
  2. 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

関連情報