목적
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()
호출할 수 있으면 정렬됩니다. 이는 두 가지 질문을 남깁니다:
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