zsh 스크립트에서 사용자 정의 함수를 sudo할 수 없습니다.

zsh 스크립트에서 사용자 정의 함수를 sudo할 수 없습니다.

.dot 파일(.dot 디렉터리 아님)을 디렉터리에 복사하고 싶습니다. 이렇게 하려면 운영 체제(MacOS)에서 사용자 비밀번호를 입력해야 합니다. 저는 이 작업을 수행하는 함수를 만들었고 이를 다음과 같은 더 큰 백업 스크립트에 통합하려고 합니다.

#!/bin/zsh
source ~/.zshrc

set -o errexit
set -o nounset
set -o pipefail

copy_dotfiles()
{
  MAIN_PATH="/Users/user01/"
  DOTFILE_DIR="${MAIN_PATH}Documents/dot_files"
  echo $DOTFILE_DIR
  mkdir $DOTFILE_DIR
  for f in $MAIN_PATH.[!.]*; do
    if [[ -f $f ]]; then
      echo "$f" is a file
      DOTFILE=`echo $f | sed 's/\/Users\/user01\///g'`
      echo "$DOTFILE" is a file
      cp $f ${DOTFILE_DIR}/${DOTFILE} || exit 1
    elif [[ -d $f ]]; then
      echo "$f" is a dir
      continue
    else
      echo "$f is not valid"
      exit 1
    fi
  done
}

sudo copy_dotfiles || exit 1

그러나 스크립트를 실행하면 다음과 같은 결과가 나타납니다.

sudo: copy_dotfiles: command not found

답변1

sudo쉘 함수가 아닙니다. 실행 파일을 실행하는 별도의 실행 파일입니다. :)

따라서 쉘 기능을 실행할 수 없습니다. 그래도 쉘 스크립트를 실행할 수 있습니다! 따라서 작동하도록 하려면 sudo이 스크립트에서 이를 생략하고 sudo를 통해 전체 스크립트를 실행하십시오.

정말source ~/.zshrc어쨌든 삭제해야 하지만특히스크립트를 루트로 실행할 때.

답변2

이 코드에 대한 인라인 주석:

#!/bin/zsh
source ~/.zshrc

호출하는 사용자의 대화형 셸 사용자 정의 파일을 스크립트로 가져오려는 이유는 무엇입니까? 이것은 말이 되지 않습니다.

set -o errexit

설정했지만 취소된 상태에서 효과를 errexit사용하려고 시도했습니다 .somefunction || ...errexit

set -o nounset
set -o pipefail

set -o errexit -o nounset -o pipefailPOSIX 셸에서 이 작업을 수행 할 수 있는 것은 아닙니다 .setopt errexit nounset pipefail

copy_dotfiles()
{
  MAIN_PATH="/Users/user01/"
  DOTFILE_DIR="${MAIN_PATH}Documents/dot_files"

이름이 모두 대문자인 변수는 일반적으로 환경 변수나 glob 범위의 변수에 사용됩니다. 함수에서 전역 변수를 고정된 값으로 설정하는 것이 이상해 보입니다.

copy_dotfiles() { # Arg: username
  local target_dir=~$1/Documents/dot_files

사용자 이름을 매개변수로 사용 copy_dotfiles하고 홈 디렉터리에 대상 디렉터리의 로컬 변수를 설정하는 것이 더 합리적입니다.

매개변수를 대상으로 하지 않는 함수를 정의하는 것은 의미가 없습니다.

  echo $DOTFILE_DIR

인쇄하기 전에 매개변수를 주의 깊게 echo변환하십시오. 사용하는 것이 가장 좋습니다: print -r -- $DOTFILE_DIR또는printf '%s\n' $DOTFILE_DIR

  mkdir $DOTFILE_DIR

이를 실행하면 대상 사용자 대신 해당 사용자를 사용하여 새 디렉터리가 생성됩니다 root. root함수의 종료 상태를 확인하지 않고 mkdir함수 errexit의 종료 상태를 처리하는 경우 해당 함수가 취소되므로 나머지 코드가 mkdir실패할 수 있습니다. 예를 들어 사용자가 민감한 시스템에 대한 심볼릭 링크를 생성했거나 ~/Documents/dot_files민감한 파일에 대한 심볼릭 링크가 있는 파일이 포함된 디렉터리를 만든 경우 심각한 결과를 초래할 수 있습니다 /etc/shadow./root/.bashrc

또한, 로 끝나는 경우에는 cmd $anything_dynamic원칙적 cmd -- $anything_dynamic으로 .$anything_dynamic-

  for f in $MAIN_PATH.[!.]*; do

$dir/file-within변수 값에 후행 문자열을 추가 하고 do를 수행하는 대신 pathname/dir 변수를 디렉터리 경로로 정의하고 이를 사용하는 것이 더 일반적입니다 ./${dir}file-within

.또한 zsh glob에는 nor 가 포함되지 않습니다 ... zsh에서는 파일을 제외 하고 제외 하더라도 부정 설정 ^보다 우선합니다 .!.[!.]*.....file

N빈 목록을 허용하고 지역 변수를 선언하기 위해 glob 한정자를 사용할 수도 있습니다 f.

local f
for f in $source_dir/.*(N); do
    if [[ -f $f ]]; then
      echo "$f" is a file

더 좋아해요 print -r -- $f is a regular file or symlink to regular file.

또한 zshglob은 유형별로 파일을 선택할 수 있으므로 여기서 반복할 필요가 없습니다 [[...]].

      DOTFILE=`echo $f | sed 's/\/Users\/user01\///g'`
      echo "$DOTFILE" is a file
      cp $f ${DOTFILE_DIR}/${DOTFILE} || exit 1

줄 기반이고 파일 경로가 여러 줄로 구성될 수 있으므로 다른 텍스트 유틸리티를 사용하여 파일 경로를 수정하는 sed것은 일반적으로 실수입니다 . 텍스트 교체 연산자( ksh 및 csh 포함)가 내장되어 있으며 경로의 꼬리를 가져오지만 여기서는 둘 다 수행하므로 교체를 수행할 필요가 없습니다.sedzsh${var/pattern/replacement}$var:s/string/replacement/$var:tcp에게 복사그리고에게 복사.

cp -- $f $target_dir/

$f해당 경로가 저장된 디렉토리에 복사됩니다 $target_dir.

그러나 루트로 실행하면 위험할 수 있습니다.

    elif [[ -d $f ]]; then
      echo "$f" is a dir
      continue
    else
      echo "$f is not valid"
      exit 1
    fi
  done
}

sudo copy_dotfiles || exit 1

sudo실행할 명령 파일의 이름이나 경로를 사용하는 외부 명령입니다. 쉘이나 다른 프로그래밍 언어에서는 함수 이름을 얻을 수 없습니다.

쉘을 실행하도록 지시할 수 있으며, 쉘은 다음을 사용할 수 있는 함수를 정의하고 호출합니다.

exec sudo zsh -o errexit -o nounset -o pipefail -c '
  '"$(typeset -f -- copy_dotfile)"'
  "$0" "$@"' copy_dotfile "$@"

하지만 여기에서는 전체 스크립트가 바로 해당 함수이므로 루트로 호출할 수도 있고, 스크립트가 슈퍼유저가 아닌 경우 루트로 다시 실행해 볼 수도 있습니다.

#! /bin/zsh -
set -o nounset

# reexecute as root if effecive uid is not 0 (not privileged).
(( EUID == 0 )) || exec sudo -u root -- "$0" "$@"

user=${1?}

ERRNO=0 USERNAME=$user # drop privileges by switching all uids and gids
                       # to those of the target user
(( ERRNO )) && exit 1 # abort if we couldn't drop privileges

# cd to the source directory
cd -P -- ~$user || exit

# hidden regular (.) files.
dotfiles=( .*(N.) )

destdir=Documents/dot_files
mkdir -p -- $destdir || exit

if (( $#dotfiles )) exec cp -pP -- $dotfiles $destdir/

(테스트되지 않음)

관련 정보