목적

목적

목적

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

  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

관련 정보