UFO ET IT

다른 스크립트를 포함하는 가장 좋은 방법은 무엇입니까?

ufoet 2023. 4. 8. 14:24
반응형

다른 스크립트를 포함하는 가장 좋은 방법은 무엇입니까?

일반적으로 스크립트를 포함하는 방법은 "source"를 사용하는 것입니다.

예:

main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

incl.sh:

echo "The included script"

「.main.sh」를 실행했을 때의 출력은 다음과 같습니다.

The included script
The main script

... 다른 위치에서 셸 스크립트를 실행하려고 하면 경로에 있지 않으면 포함을 찾을 수 없습니다.

스크립트가 포함 스크립트를 찾을 수 있도록 하는 좋은 방법은 무엇입니까?특히 스크립트를 이식할 필요가 있는 경우.

저는 모든 대본을 상대적인 것으로 만드는 경향이 있습니다.이렇게 하면 dirname을 사용할 수 있습니다.

#!/bin/sh

my_dir="$(dirname "$0")"

"$my_dir/other_script.sh"

파티에 늦은 건 알지만 스크립트를 어떻게 시작하고 빌트인을 독점적으로 사용하든 상관없습니다.

DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"

.는 (dot) 명령어에 에일리어스입니다.source,$PWD디렉토리의 입니다.BASH_SOURCE가 소스 입니다.${string%substring}는 $string의 에서 $합니다.

대체 수단:

scriptPath=$(dirname $0)

다음과 같습니다.

scriptPath=${0%/*}

..기본 명령어가 아닌 dirname에 의존하지 않는 것이 장점입니다(또한 에뮬레이터에서 항상 사용할 수 있는 것은 아닙니다).

는, 「동일 디렉토리」, 「동일 디렉토리」를 할 수 .dirname $0:

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"

Chris Boran의 방법을 사용하는 것이 가장 좋은 방법이라고 생각합니다만, MY_DIR는 다음과 같이 계산해야 합니다.

#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh

readlink의 man 페이지를 인용하려면:

readlink - display value of a symbolic link

...

  -f, --canonicalize
        canonicalize  by following every symlink in every component of the given 
        name recursively; all but the last component must exist

는 지금까지 지 i i i i i i i 。MY_DIR가 올바르게 계산되지 않았습니다.의 심볼링크를 $PATH그건 효과가 있다.

이 질문에 대한 답변을 조합하면 가장 강력한 솔루션을 얻을 수 있습니다.

의존관계 및 디렉토리 구조를 크게 지원하는 프로덕션급 스크립트로도 사용할 수 있었습니다.

#!/bin/module
# 현재 스크립트의 풀패스THIS='readlink -f "${BASH_SOURCE[0]}" 2>/dev/disc||disclink $0"
# 현재 스크립트가 있는 디렉토리DIR='dirname "${[THIS]"
# '점'은 '소스'를 의미합니다. 즉, '포함':
. "$DIR/compile.sh"

이 메서드는 다음 모두를 지원합니다.

  • 경로 공간
  • 링크(경유)readlink)
  • ${BASH_SOURCE[0]} 튼튼하다$0
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"

1. 가장 깔끔한

저는 거의 모든 제안을 검토했습니다.여기서 저에게 가장 잘 맞는 제안을 소개하겠습니다.

script_root=$(dirname $(readlink -f $0))

는 문자와 되어 있는 할 수 있습니다.$PATH디렉토리로 이동합니다.

자세한 것은, https://github.com/pendashteh/hcagent/blob/master/bin/hcagent 를 참조해 주세요.

2. 가장 쿨한

# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')

이것은 실제로 이 페이지의 다른 답변에서 나온 것이지만, 저도 제 답변에 추가하고 있습니다!

3. 가장 신뢰할 수 있는 것

또는 드물게 이러한 방법이 작동하지 않는 경우에는 다음과 같은 방탄 방식을 사용할 수 있습니다.

# Copyright http://stackoverflow.com/a/7400673/257479
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

script_root=$(dirname $(whereis_realpath "$0"))

행해지고 에서 볼 수 .taskrunner출처 : https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner

이것이 누군가에게 도움이 되기를 바랍니다:)

또한 해당 운영체제와 에뮬레이터가 작동하지 않을 경우 코멘트로 남겨주세요.감사합니다!

이것은, 스크립트가 다음의 소스인 경우에서도 동작합니다.

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"

다른 스크립트의 위치를 지정해야 합니다.다른 방법은 없습니다.스크립트 상단에 설정 가능한 변수를 권장합니다.

#!/bin/bash
installpath=/where/your/scripts/are

. $installpath/incl.sh

echo "The main script"

또는 PROG_HOME이나 somesuch와 같이 사용자가 프로그램 홈의 위치를 나타내는 환경 변수를 유지하도록 할 수 있습니다.이 정보는 /etc/profile.d/에 해당 정보를 포함하는 스크립트를 작성함으로써 사용자에게 자동으로 제공할 수 있습니다.이 스크립트는 사용자가 로그인할 때마다 생성됩니다.

시스템 전체의 다양한 컴포넌트에 로케이션을 제공하는 것을 목적으로 하는 setenv 스크립트를 작성하는 것을 추천합니다.

setenv 스크립트를 사용하는 모든 스크립트에서 모든 위치가 공통되도록 다른 모든 스크립트에서 이 스크립트의 소스가 됩니다.

이는 cronjobs를 실행할 때 매우 유용합니다.cron을 실행하면 최소한의 환경을 얻을 수 있지만 먼저 모든 cron 스크립트에 setenv 스크립트를 포함하도록 설정하면 cronjobs를 실행할 환경을 제어하고 동기화할 수 있습니다.

우리는 약 2,000 kSLOC의 프로젝트에 걸쳐 지속적인 통합에 사용된 빌드 원숭이에 이러한 기술을 사용했습니다.

Shell Script Loader는 이를 위한 솔루션입니다.

include()라는 이름의 함수를 제공합니다.이 함수는 여러 스크립트에서 여러 번 호출하여 단일 스크립트를 참조할 수 있지만 스크립트는 한 번만 로드됩니다.함수는 완전한 경로 또는 부분 경로를 받아들일 수 있습니다(스크립트는 검색 경로에서 검색됨).load()라는 이름의 유사한 함수도 제공됩니다.이 함수는 스크립트를 무조건 로드합니다.

bash, ksh, pd ksh, zsh 각각에 최적화된 스크립트 및 ash, 대시, gareboom sh일반적으로 원래의 sh와 호환되는 다른 셸에 대해 셸이 제공하는 기능에 따라 기능을 자동으로 최적화하는 유니버설스크립트를 통해 동작합니다.

[비겁한 예]

start.sh

이것은 옵션의 스타터 스크립트입니다.기동 방법을 여기에 배치하는 것은 편리할 뿐, 대신에 메인 스크립트에 배치할 수 있습니다.이 스크립트는 스크립트를 컴파일하는 경우에도 필요하지 않습니다.

#!/bin/sh

# load loader.sh
. loader.sh

# include directories to search path
loader_addpath /usr/lib/sh deps source

# load main script
load main.sh

main.sh

include a.sh
include b.sh

echo '---- main.sh ----'

# remove loader from shellspace since
# we no longer need it
loader_finish

# main procedures go from here

# ...

a.sh

include main.sh
include a.sh
include b.sh

echo '---- a.sh ----'

b.sh

include main.sh
include a.sh
include b.sh

echo '---- b.sh ----'

출력:

---- b.sh ----
---- a.sh ----
---- main.sh ----

가장 좋은 점은 이를 기반으로 한 스크립트를 컴파일하여 사용 가능한 컴파일러와 단일 스크립트를 형성할 수도 있다는 것입니다.

다음은 그것을 사용하는 프로젝트입니다.http://sourceforge.net/p/playshell/code/ci/master/tree/스크립트를 컴파일하거나 컴파일하지 않고 포터블하게 실행할 수 있습니다.단일 스크립트를 생성하기 위한 컴파일도 수행할 수 있으며 설치 시 유용합니다.

또, 구현 스크립트의 구조를 간단하게 알고 싶은 보수 정당을 위한 심플한 프로토타입도 작성했습니다.https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype.bash이것은 작고 누구나 자신의 코드가 Bash 4.0 이후와 함께 실행되도록 의도된 경우 메인 스크립트에 코드를 포함할 수 있으며, 또한 사용하지 않습니다.eval.

Steve의 응답은 올바른 기술이지만 설치 경로 변수가 이러한 선언이 이루어지는 별도의 환경 스크립트에 포함되도록 리팩터링해야 합니다.

그러면 모든 스크립트가 해당 스크립트의 소스이며 installpath를 변경해야 합니다.한 곳에서만 변경하면 됩니다.미래를 대비할 수 있게 해 주죠세상에, 그 단어 정말 싫어!

BTW 예시와 같이 사용할 경우 ${installpath}를 사용하여 변수를 참조해야 합니다.

. ${installpath}/incl.sh

중괄호를 생략하면 일부 셸이 변수 "installpath/incl.sh"을 확장하려고 시도합니다.

모든 스타트업 스크립트를 .bashrc.d 디렉토리에 저장합니다.이것은 /etc/profile.d 등의 장소에서 일반적인 기술입니다.

while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE

글로브를 이용한 해결책의 문제점은...

for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done

파일 목록이 너무 길 수 있습니다.이런 접근법...

find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done

...환경은 원하는 대로 변화하지 않습니다.

이것은 신뢰성 있게 동작합니다.

source_relative() {
 local dir="${BASH_SOURCE%/*}"
 [[ -z "$dir" ]] && dir="$PWD"
 source "$dir/$1"
}

source_relative incl.sh

source 또는 $0을 사용해도 스크립트의 실제 경로를 알 수 없습니다.스크립트의 프로세스 ID를 사용하여 실제 경로를 검색할 수 있습니다.

ls -l       /proc/$$/fd           | 
grep        "255 ->"            |
sed -e      's/^.\+-> //'

이 스크립트를 사용하고 있기 때문에, 항상 도움이 됩니다.

물론 각자 다르겠지만, 아래 블록은 꽤 단단한 것 같아요.여기에는 디렉토리를 찾는 "최적의" 방법과 다른 bash 스크립트를 호출하는 "최적의" 방법이 포함됩니다.

scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh

echo "The main script"

따라서 이것이 다른 스크립트를 포함시키는 "최적의" 방법일 수 있습니다.이는 bash 스크립트의 저장 위치를 알려주는 다른 "최적의" 답변을 기반으로 합니다.

개인적으로는 모든 라이브러리를lib폴더 및 사용import로드하는 기능을 수행합니다.

폴더 구조

여기에 이미지 설명 입력

script.sh내용물

# Imports '.sh' files from 'lib' directory
function import()
{
  local file="./lib/$1.sh"
  local error="\e[31mError: \e[0mCannot find \e[1m$1\e[0m library at: \e[2m$file\e[0m"
  if [ -f "$file" ]; then
     source "$file"
    if [ -z $IMPORTED ]; then
      echo -e $error
      exit 1
    fi
  else
    echo -e $error
    exit 1
  fi
}

이 Import 기능은 스크립트의 시작 부분에 있어야 하며 다음과 같이 라이브러리를 쉽게 Import할 수 있습니다.

import "utils"
import "requirements"

각 라이브러리의 맨 위에 한 줄씩 추가합니다(유틸리티).sh) :

IMPORTED="$BASH_SOURCE"

이제 내부 기능에 액세스할 수 있습니다.utils.sh그리고.requirements.sh부터script.sh

TODO: 링커를 작성하여 싱글을 구축합니다.sh파일

incl.sh과 메인 폴더가 어디에 있는지 찾으면 됩니다.sh가 저장됩니다.메인만 변경해주세요sh:

main.sh

#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")"
source $SCRIPT_DIR/incl.sh

echo "The main script"

스크립트에 적합한 장소는 다음과 같습니다./usr/local/lib/

/usr/local/lib

로컬에 설치된 프로그램과 관련된 파일입니다.

저는 개인적으로/usr/local/lib/bash/includes를 포함합니다.이와 같이 libs를 포함하기 위한 bash-helper lib가 있습니다.

#!/bin/bash

. /usr/local/lib/bash/includes/bash-helpers.sh

include api-client || exit 1                   # include shared functions
include mysql-status/query-builder || exit 1   # include script functions

# include script functions with status message
include mysql-status/process-checker; status 'process-checker' $? || exit 1
include mysql-status/nonexists; status 'nonexists' $? || exit 1

bash-param에는 상태 출력이 포함됩니다.

내가 여기서 본 대부분의 답변들은 너무 복잡해 보인다.이 방법은 항상 안정적으로 작동했습니다.

FULLPATH=$(readlink -f $0)
INCPATH=${FULLPATH%/*}

INCPATH는 스크립트의 호출 방법($PATH, relative 또는 absolute)에 관계없이 스크립트 파일 이름을 제외한 스크립트의 전체 경로를 유지합니다.

그 후, 같은 디렉토리에 파일을 포함시키기 위해서만, 다음의 조작을 실시할 필요가 있습니다.

. $INCPATH/file_to_include.sh

레퍼런스:TecPorto / Location에 의존하지 않는 것

여기 당신이 사용할 수 있는 좋은 기능이 있습니다.@sacii가 만든 것을 기반으로 합니다.감사해요.

소스(스크립트 호출에 포함)에 공백으로 구분된 임의의 수의 스크립트 이름을 나열할 수 있습니다.source_files).

옵션으로 첫 번째 인수로 절대 경로 또는 상대 경로를 전달할 수 있습니다.이 경로는 대신 여기에서 생성됩니다.

여러 번 호출할 수 있으며(아래 예 참조), 다른 dir에서 스크립트를 송신할 수 있습니다.

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

예를 들어, 이 예제를 실행하면 이 예제의 사용 방법을 알 수 있습니다.

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

## -- EXAMPLE -- ##
# assumes dir structure:
# /
#   source_files.sh
#   sibling.sh
#   scripts/
#     child.sh
#   nested/
#     scripts/
#       grandchild.sh

cd /tmp || exit 1

# sibling.sh
tee sibling.sh <<- EOF > /dev/null 
  #!/usr/bin/env bash
  
  export SIBLING_VAR='sibling var value'
EOF

# scripts/child.sh
mkdir -p scripts
tee scripts/child.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export CHILD_VAR='child var value'
EOF

# nested/scripts/grandchild.sh
mkdir -p nested/scripts
tee nested/scripts/grandchild.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export GRANDCHILD_VAR='grandchild var value'
EOF

source_files 'sibling'
source_files 'scripts' 'child'
source_files 'nested/scripts' 'grandchild'

echo "$SIBLING_VAR"
echo "$CHILD_VAR"
echo "$GRANDCHILD_VAR"

rm sibling.sh
rm -rf scripts nested

cd - || exit 1

인쇄:

sibling var value
child var value
grandchild var value

다음 항목도 사용할 수 있습니다.

PWD=$(pwd)
source "$PWD/inc.sh"

언급URL : https://stackoverflow.com/questions/192292/how-best-to-include-other-scripts

반응형