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とにかく削除する必要がありますが、特にスクリプトをrootとして実行するとき。

答え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

これを実行すると、ターゲットユーザーの代わりにそのユーザーを使用して新しいディレクトリが作成されますrootroot関数の終了状態を確認せずに関数の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

ただし、rootとして実行すると危険です。

    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 "$@"

しかし、ここではスクリプト全体がまさにその関数なので、rootとして呼び出すこともできますし、スクリプトがスーパーユーザーでない場合はrootとして再実行してみることもできます。

#! /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/

(テストされていません)

関連情報