Programing

Bash 스크립트의 의미론?

lottogame 2020. 9. 14. 21:37
반응형

Bash 스크립트의 의미론?


내가 아는 다른 어떤 언어보다 더 작은 것이 필요할 때마다 인터넷 검색으로 Bash를 "배웠습니다". 따라서 작동하는 것처럼 보이는 작은 스크립트를 함께 패치 워크 할 수 있습니다. 그러나 나는 무슨 일이 일어나고 있는지 정말로 알지 못하고 프로그래밍 언어로서 Bash에 대한보다 공식적인 소개를 바라고 있었다. 예 : 평가 순서는 무엇입니까? 범위 지정 규칙은 무엇입니까? 타이핑 규칙은 무엇입니까? 예를 들어 모든 것이 문자열입니까? 프로그램의 상태는 무엇입니까? 변수 이름에 문자열을 키-값으로 할당하는 것입니다. 그 이상이 있습니까, 예를 들어 스택? 힙이 있습니까? 등등.

이런 식의 통찰을 얻기 위해 GNU Bash 매뉴얼을 참조하려고했지만 내가 원하는 것이 아닌 것 같습니다. 핵심 시맨틱 모델에 대한 설명 이라기보다는 통사론 적 설탕 목록에 가깝습니다. 백만개가 넘는 온라인 "bash 자습서"는 더 나쁩니다. 아마도 나는 먼저 공부 sh하고 Bash를 이것 위에 통사론 적 설탕으로 이해해야할까요? 하지만 이것이 정확한 모델인지는 모르겠습니다.

어떤 제안?

편집 : 이상적으로 내가 찾고있는 것에 대한 예를 제공하라는 요청을 받았습니다. 내가 "형식적 의미론"이라고 생각하는 것에 대한 다소 극단적 인 예는 "자바 스크립트의 본질"에 대한이 문서입니다 . 아마도 약간 덜 공식적인 예는 Haskell 2010 보고서 입니다.


쉘은 운영 체제의 인터페이스입니다. 일반적으로 그 자체로는 다소 강력한 프로그래밍 언어이지만 특히 운영 체제 및 파일 시스템과 쉽게 상호 작용할 수 있도록 설계된 기능이 있습니다. POSIX 쉘 (이하 "쉘"이라고 함) 의미는 LISP의 일부 기능 (s- 표현식은 쉘 단어 분할 과 많은 공통점이 있음 ) 및 C (쉘의 산술 구문 의 대부분)를 결합한 약간의 멍청한 의미 입니다. 의미론은 C)에서 비롯됩니다.

셸 구문의 또 다른 루트는 개별 UNIX 유틸리티의 혼란으로 발전한 것입니다. 쉘에 내장 된 대부분의 기능은 실제로 외부 명령으로 구현 될 수 있습니다. /bin/[많은 시스템에 존재 한다는 것을 깨닫게되면 많은 쉘 초보자를 루프로 던집니다 .

$ if '/bin/[' -f '/bin/['; then echo t; fi # Tested as-is on OS X, without the `]`
t

와트?

쉘이 구현되는 방법을 보면 훨씬 더 의미가 있습니다. 여기 내가 연습으로 한 구현이 있습니다. 파이썬으로되어 있지만 누구에게도 끊이지 않기를 바랍니다. 그다지 강력하지는 않지만 유익합니다.

#!/usr/bin/env python

from __future__ import print_function
import os, sys

'''Hacky barebones shell.'''

try:
  input=raw_input
except NameError:
  pass

def main():
  while True:
    cmd = input('prompt> ')
    args = cmd.split()
    if not args:
      continue
    cpid = os.fork()
    if cpid == 0:
      # We're in a child process
      os.execl(args[0], *args)
    else:
      os.waitpid(cpid, 0)

if __name__ == '__main__':
  main()

나는 위의 내용이 쉘의 실행 모델이 거의임을 분명히하기를 바랍니다.

1. Expand words.
2. Assume the first word is a command.
3. Execute that command with the following words as arguments.

확장, 명령 해결, 실행. 쉘의 모든 의미는 위에서 작성한 구현보다 훨씬 풍부하지만이 세 가지 중 하나에 묶여 있습니다.

모든 명령이 아닙니다 fork. 사실, 외부로 구현 된 (그렇게해야 할 것 같은) 의미 가없는 몇 가지 명령이 fork있지만 심지어 엄격한 POSIX 준수를 위해 외부로 사용할 수있는 경우가 많습니다.

Bash는 POSIX 셸을 향상시키기 위해 새로운 기능과 키워드를 추가하여이 기반을 구축합니다. sh와 거의 호환이 가능하며 bash는 매우 유비쿼터스이므로 일부 스크립트 작성자는 스크립트가 실제로 POSIX로 엄격한 시스템에서 작동하지 않을 수 있다는 사실을 깨닫지 못한 채 몇 년이 걸립니다. (나는 또한 사람들이 하나의 프로그래밍 언어의 의미와 스타일에 대해 그렇게 많은 관심을 갖고 쉘의 의미와 스타일에 대해 그렇게별로 신경을 쓰지 않는지 궁금합니다.

평가 순서

이것은 약간의 트릭 질문입니다. Bash는 기본 구문의 표현식을 왼쪽에서 오른쪽으로 해석하지만 산술 구문에서는 C 우선 순위를 따릅니다. 하지만 식은 확장 과 다릅니다 . EXPANSIONbash 매뉴얼 섹션에서 :

확장 순서는 다음과 같습니다. 중괄호 확장; 물결표 확장, 매개 변수 및 변수 확장, 산술 확장 및 명령 대체 (왼쪽에서 오른쪽으로 수행) 단어 분할; 및 경로 이름 확장.

단어 분리, 경로명 확장 및 매개 변수 확장을 이해하고 있다면 bash가 수행하는 대부분의 작업을 잘 이해하고있는 것입니다. 단어 분리 후에 오는 경로 이름 확장은 이름에 공백이있는 파일이 여전히 glob과 일치 할 수 있도록 보장하기 때문에 중요합니다. 이것이 일반적으로 glob 확장을 잘 사용하는 것이 일반적으로 명령 구문 분석 보다 나은 이유 입니다.

범위

기능 범위

이전 ECMAscript와 마찬가지로 셸은 함수 내에서 명시 적으로 이름을 선언하지 않는 한 동적 범위를 갖습니다.

$ foo() { echo $x; }
$ bar() { local x; echo $x; }
$ foo

$ bar

$ x=123
$ foo
123
$ bar

$ …

환경 및 프로세스 "범위"

서브 쉘은 부모 쉘의 변수를 상속하지만 다른 종류의 프로세스는 내 보내지 않은 이름을 상속하지 않습니다.

$ x=123
$ ( echo $x )
123
$ bash -c 'echo $x'

$ export x
$ bash -c 'echo $x'
123
$ y=123 bash -c 'echo $y' # another way to transiently export a name
123

다음 범위 지정 규칙을 결합 할 수 있습니다.

$ foo() {
>   local -x bar=123 # Export foo, but only in this scope
>   bash -c 'echo $bar'
> }
$ foo
123
$ echo $bar

$

타이핑 규율

음, 유형. 네. Bash에는 실제로 유형이 없으며 모든 것이 문자열로 확장됩니다 (또는 단어 가 더 적절할 것입니다.) 그러나 다른 유형의 확장을 살펴 보겠습니다.

문자열

거의 모든 것이 문자열로 취급 될 수 있습니다. bash의 베어 워드는 그 의미가 적용된 확장에 전적으로 의존하는 문자열입니다.

확장 없음

겉으로 드러나지 않은 단어는 실제로 단어 일 뿐이며 따옴표는 그것에 대해 아무것도 바꾸지 않는다는 것을 입증하는 것이 좋습니다.

$ echo foo
foo
$ 'echo' foo
foo
$ "echo" foo
foo
부분 문자열 확장
$ fail='echoes'
$ set -x # So we can see what's going on
$ "${fail:0:-2}" Hello World
+ echo Hello World
Hello World

확장에 대한 자세한 내용 Parameter Expansion은 설명서 섹션을 참조하십시오. 꽤 강력합니다.

정수 및 산술 표현식

정수 속성으로 이름을 추가하여 할당 표현식의 오른쪽을 산술로 처리하도록 쉘에 지시 할 수 있습니다. 그런 다음 매개 변수가 확장 될 때 문자열로 확장되기 전에 정수 수학으로 평가됩니다.

$ foo=10+10
$ echo $foo
10+10
$ declare -i foo
$ foo=$foo # Must re-evaluate the assignment
$ echo $foo
20
$ echo "${foo:0:1}" # Still just a string
2

배열

인수 및 위치 매개 변수

배열에 대해 이야기하기 전에 위치 매개 변수에 대해 논의 할 가치가 있습니다. 쉘 스크립트의 인수는 숫자 매개 변수를 사용하여 액세스 할 수 있습니다 $1, $2, $3, 등 당신은 사용하여 한 번에 모든 매개 변수에 액세스 할 수 있습니다 "$@"배열과 많은 공통점을 가지고있는 확장. set또는 shift내장을 사용하거나 단순히 다음 매개 변수로 쉘 또는 쉘 함수를 호출 하여 위치 매개 변수를 설정하고 변경할 수 있습니다 .

$ bash -c 'for ((i=1;i<=$#;i++)); do
>   printf "\$%d => %s\n" "$i" "${@:i:1}"
> done' -- foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showpp() {
>   local i
>   for ((i=1;i<=$#;i++)); do
>     printf '$%d => %s\n' "$i" "${@:i:1}"
>   done
> }
$ showpp foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showshift() {
>   shift 3
>   showpp "$@"
> }
$ showshift foo bar baz biz quux xyzzy
$1 => biz
$2 => quux
$3 => xyzzy

bash 매뉴얼은 때때로 $0위치 매개 변수 라고도합니다 . 인수 count에 포함되지 않았기 때문에 혼란 스럽지만 $#번호가 매겨진 매개 변수이므로 meh. $0쉘 또는 현재 쉘 스크립트의 이름입니다.

배열

배열의 구문은 위치 매개 변수를 모델로하므로 원하는 경우 배열을 "외부 위치 매개 변수"의 명명 된 종류로 생각하는 것이 좋습니다. 다음 접근 방식을 사용하여 배열을 선언 할 수 있습니다.

$ foo=( element0 element1 element2 )
$ bar[3]=element3
$ baz=( [12]=element12 [0]=element0 )

인덱스로 배열 요소에 액세스 할 수 있습니다.

$ echo "${foo[1]}"
element1

배열을 분할 할 수 있습니다.

$ printf '"%s"\n' "${foo[@]:1}"
"element1"
"element2"

배열을 일반 매개 변수로 취급하면 0 번째 인덱스를 얻게됩니다.

$ echo "$baz"
element0
$ echo "$bar" # Even if the zeroth index isn't set

$ …

단어 분리를 방지하기 위해 따옴표 또는 백 슬래시를 사용하는 경우 배열은 지정된 단어 분리를 유지합니다.

$ foo=( 'elementa b c' 'd e f' )
$ echo "${#foo[@]}"
2

배열과 위치 매개 변수의 주요 차이점은 다음과 같습니다.

  1. Positional parameters are not sparse. If $12 is set, you can be sure $11 is set, too. (It could be set to the empty string, but $# will not be smaller than 12.) If "${arr[12]}" is set, there's no guarantee that "${arr[11]}" is set, and the length of the array could be as small as 1.
  2. The zeroth element of an array is unambiguously the zeroth element of that array. In positional parameters, the zeroth element is not the first argument, but the name of the shell or shell script.
  3. To shift an array, you have to slice and reassign it, like arr=( "${arr[@]:1}" ). You could also do unset arr[0], but that would make the first element at index 1.
  4. Arrays can be shared implicitly between shell functions as globals, but you have to explicitly pass positional parameters to a shell function for it to see those.

It's often convenient to use pathname expansions to create arrays of filenames:

$ dirs=( */ )

Commands

Commands are key, but they're also covered in better depth than I can by the manual. Read the SHELL GRAMMAR section. The different kinds of commands are:

  1. Simple Commands (e.g. $ startx)
  2. Pipelines (e.g. $ yes | make config) (lol)
  3. Lists (e.g. $ grep -qF foo file && sed 's/foo/bar/' file > newfile)
  4. Compound Commands (e.g. $ ( cd -P /var/www/webroot && echo "webroot is $PWD" ))
  5. Coprocesses (Complex, no example)
  6. Functions (A named compound command that can be treated as a simple command)

Execution Model

The execution model of course involves both a heap and a stack. This is endemic to all UNIX programs. Bash also has a call stack for shell functions, visible via nested use of the caller builtin.

References:

  1. The SHELL GRAMMAR section of the bash manual
  2. The XCU Shell Command Language documentation
  3. The Bash Guide on Greycat's wiki.
  4. Advanced Programming in the UNIX Environment

Please make comments if you want me to expand further in a specific direction.


The answer to your question "What is the typing discipline, e.g. is everything a string" Bash variables are character strings. But, Bash permits arithmetic operations and comparisons on variables when variables are integers. The exception to rule Bash variables are character strings is when said variables are typeset or declared otherwise

$ A=10/2
$ echo "A = $A"           # Variable A acting like a String.
A = 10/2

$ B=1
$ let B="$B+1"            # Let is internal to bash.
$ echo "B = $B"           # One is added to B was Behaving as an integer.
B = 2

$ A=1024                  # A Defaults to string
$ B=${A/24/STRING01}      # Substitute "24"  with "STRING01".
$ echo "B = $B"           # $B STRING is a string
B = 10STRING01

$ B=${A/24/STRING01}      # Substitute "24"  with "STRING01".
$ declare -i B
$ echo "B = $B"           # Declaring a variable with non-integers in it doesn't change the contents.
B = 10STRING01

$ B=${B/STRING01/24}      # Substitute "STRING01"  with "24".
$ echo "B = $B"
B = 1024

$ declare -i B=10/2       # Declare B and assigning it an integer value
$ echo "B = $B"           # Variable B behaving as an Integer
B = 5

Declare option meanings:

  • -a Variable is an array.
  • -f Use function names only.
  • -i The variable is to be treated as an integer; arithmetic evaluation is performed when the variable is assigned a value.
  • -p Display the attributes and values of each variable. When -p is used, additional options are ignored.
  • -r Make variables read-only. These variables cannot then be assigned values by subsequent assignment statements, nor can they be unset.
  • -t Give each variable the trace attribute.
  • -x Mark each variable for export to subsequent commands via the environment.

The bash manpage has quite a bit more info than most manpages, and includes some of what you're asking for. My assumption after more than a decade of scripting bash is that, due to its' history as an extension of sh, it has some funky syntax (to maintain backward compatibility with sh).

FWIW, my experience has been like yours; although the various books (e.g., O'Reilly "Learning the Bash Shell" and similar) do help with the syntax, there are lots of strange ways of solving various problems, and some of them are not in the book and must be googled.

참고URL : https://stackoverflow.com/questions/23207168/a-semantics-for-bash-scripts

반응형