如何顯示提示並防止執行基本但危險的命令,如 mv 或 rm
客觀的
每當我使用
mv
(或類似性質的命令)時,我希望我的 shell 提示我類似“嘿,你使用了 mv。對不起,但使用 mv -i 再做一次”。我想知道完成這種互動的合理方法。目的是讓我的電腦每次使用都會抱怨,
mv
所以我必須重新輸入mv -i
. 這樣我就會發展肌肉記憶進入mv -i
。讓我的電腦在引擎蓋下轉換我
mv
的不是目的,並且完全違背了意圖。mv -i
如果這聽起來像是一個愚蠢的想法,我想知道為什麼和更好的解決方案。
背景
在過去,我曾使用
mv
並錯誤地覆蓋了文件。雖然此評論建議以mv
實際工作方式使用別名,但mv -i
我同意此評論關於此類自定義的後果。與其習慣打字mv
,我寧願養成打字的習慣,mv -i
這樣我就不會在其他機器上搞砸了。關注點
我擔心此類修改會影響呼叫
mv
.mv
有沒有一種聰明的方法來中止並僅在我自己通過鍵入並按 Enter 執行時才顯示提示?筆記
- 我備份文件,而不是相關的
- 我知道我可以在我的桌子上貼便利貼或在牆上貼海報以提高認識
- 環境:Debian、XFCE、xfce4-terminal、Bash
雖然@glennJackman使用包裝器的方法檢查第一個參數是否以開頭
-
並包含i
在大多數常見情況下可能已經足夠好,但在某些情況下它會失敗:
- 它妨礙了
mv --help
(不是--version
包含-i
)。- 它無法檢測到
-i
inmv -v -i a b
(或者當環境中沒有時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
實現的版本之間的差異。
mv
(和大多數實用程序)的 GNU 實現將用於getopt_long()
解析它們的選項。如果您的包裝器可以
getopt_long()
使用相同的參數呼叫,我們將被排序。這留下了兩個問題:
- 我們需要
getopt_long()
在shell中找到一個介面- 我們需要弄清楚那些參數
mv
傳遞給getopt_long()
如果您在 GNU/Linux 系統上,有可能的方法。
getopt_long()
雖然在 GNU 工具箱中沒有 shell CLI ,但在util-linux
: 它與andgetopt
一起使用時的實用程序中有一個。-o``-l
在 GNU/Linux 上,GNU 實用程序將從
getopt_long()
libc 呼叫該函式,因為它將是 GNU libc,因此您可以使用ltrace
which traces library calls 來查看解析選項的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
參數,甚至隱藏我們不關心的參數。作為概念證明,這裡有一個 zsh 腳本,它會
sh
為包裝函式輸出兼容程式碼,該函式在呼叫之前檢查-i
/--interactive
(或--help
/--version
)或其縮寫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)"
在您的 shell 的互動模式配置中 (
~/.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
我會使用一個包裝函式:你可以將它添加到你的 .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 }