Різні способи виконання сценарію оболонки


44

Існує декілька способів виконання сценарію, відомі такі:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

Чи більше цього? Які відмінності між ними? Чи є ситуації, що я повинен використовувати одну, а не іншу?


Чудово знати, завдяки вашим запитанням та відповідям нижче, особливо Шона. Я хотів би додати щось, що мені не було дуже очевидним, поки я не пройшов кілька тестів. Включення "/" у другому способі вище перенесе команду в режим 1 вище. Тобто, поки "./myscript.sh" дотримується режиму 1, ". Myscript.sh" дотримується режиму 2. Ви згадали "використовуючи шлях (абсолютний або відносний)", але просто хотіли зробити це очевидним.
Арун

Відповіді:


32

Інший спосіб - зателефонувати інтерпретатору та передати шлях до сценарію до нього:

/bin/sh /path/to/script

Точка та джерело рівнозначні. (EDIT: ні, вони не такі: як вказує KeithB в коментарі до іншої відповіді, "." Працює лише в оболонках, пов'язаних з bash, де "source" працює як в оболонках bash, так і в csh.) Він виконує сценарій у -place (наче ви скопіювали та вставили скрипт прямо туди). Це означає, що будь-які функції та не локальні змінні в сценарії залишаються. Це також означає, що якщо скрипт містить компакт-диск у каталог, ви все одно будете там, коли це буде зроблено.

Інші способи запуску скрипту запуститимуть його у власній підпакеті. Змінні в сценарії ще не існують, коли це зроблено. Якщо скрипт змінив каталоги, він не впливає на середовище виклику.

/ шлях / до / скрипт та / bin / sh сценарій дещо відрізняються. Зазвичай сценарій на початку має "шебанг", який виглядає приблизно так:

#! /bin/bash

Це шлях до інтерпретатора сценарію. Якщо він визначає іншого інтерпретатора, ніж ви, виконуючи його, то він може поводитись інакше (або може не працювати взагалі).

Наприклад, сценарії Perl та сценарії Ruby починаються з (відповідно):

#! /bin/perl

і

#! /bin/ruby

Якщо ви виконаєте один із цих сценаріїв, запустивши /bin/sh script, вони взагалі не працюватимуть.

Ubuntu насправді не використовує оболонку bash, але дуже схожу з назвою dash. Сценарії, які вимагають bash, можуть спрацьовувати трохи неправильно, коли їх викликають, /bin/sh scriptтому що ви тільки що викликали скрипт bash за допомогою інтерпретатора тире.

Ще одна невелика різниця між викликом сценарію безпосередньо та передачею трактування сценарію інтерпретатору полягає в тому, що скрипт повинен бути позначений виконуваним, щоб запустити його безпосередньо, але не запускати його, передаючи шлях інтерпретатору.

Ще одна незначна зміна: ви можете приєднати будь-який із цих способів виконання сценарію з eval, так що ви можете мати

eval sh script
eval script
eval . script

і так далі. Це насправді нічого не змінює, але я подумав, що я включу це для ґрунтовності.


6
Казати, що "Ubuntu насправді не використовує оболонку bash", є неточним і технічно неправильним. Ubuntu робить використовувати Баш оболонку, справа в тому , що shвідповідає dash, але не до bash.
Faheem Mitha

@Shawn У пункті фіртів ви написали "Він виконує сценарій на місці (як би ви скопіювали та вставили скрипт прямо туди). Це означає, що будь-які функції та не локальні змінні в сценарії залишаються". що ви маєте на увазі під другим рядком? Чи можете ви поясніть, будь ласка.
Geek

@Geek, коли ви виконуєте сценарій як дочірній процес (звичайний спосіб), будь-які змінні та функції, які він визначає (це оточення), відходять, коли процес закінчується. Якщо ви джерелом сценарію, ці змінні та функції створюються в поточному середовищі; коли сценарій закінчується, зміни в середовищі залишаються.
Шон Дж. Гофф

@ ShawnJ.Goff дякую за роз’яснення. +1.
Geek

Сюжет-твіст: Борн Шелл (sh) приймає лише крапку, а не джерело, оскільки це bash-вбудований. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Тому я б сказав, що найбільш портативний спосіб - це крапка.
dezza

9

Більшість людей налагоджують сценарії оболонки, додаючи до сценарію наступні правки налагодження :

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Але це означає, що вам потрібно відкрити файл у редакторі (якщо у вас є дозволи на редагування файлу), додавши рядок типу " set -xЗберегти файл", а потім виконати файл. Потім, коли ви закінчите, вам потрібно виконати ті ж самі кроки та видалити set -xі т. Д. І т.д. Це може бути нудно.

Замість того, щоб робити все це, ви можете встановити прапорці налагодження в командному рядку:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...

2
Пов’язана порада: я звик ставити emulate sh 2>/dev/nullу верхній частині своїх сценаріїв оболонки. При запуску з zsh це переводить його в режим сумісний з POSIX. При запуску з іншими оболонками лінія не впливає. Тоді я можу запустити сценарій за допомогою zsh -x /path/to/script. Мені подобається zsh, тому що він забезпечує кращі сліди, ніж bash або ksh.
Жил "ТАК - перестань бути злим"

7

Шон Дж. Гофф зробив багато хороших моментів, але не включив всю історію:

Ubuntu насправді не використовує оболонку bash, але дуже схожу з назвою dash. Сценарії, які вимагають bash, можуть працювати трохи неправильно, коли викликаються, виконуючи /bin/shскрипт, тому що ви щойно зателефонували на bash-скрипт за допомогою інтерпретатора тире.

Багато системних сценаріїв (наприклад, init.d, in / etc тощо) мають шебанг #!/bin/sh, але /bin/shнасправді є символічним посиланням на інший оболонку - в колишні часи /bin/bash, нині /bin/dash. Але коли одного з них викликають як /bin/sh, вони поводяться інакше, тобто вони дотримуються POSIX-сумісного режиму.

Як вони це роблять? Ну, вони перевіряють, як їх викликали.

Чи може сам оболонка перевірити, як його викликали, і чи робити різні речі, залежно від цього? Так, це може. Тож спосіб, до якого ви звертаєтесь, завжди може призвести до різних результатів, але, звичайно, це рідко робиться, щоб роздратувати вас. :)

Як правило, якщо ви вивчаєте таку оболонку, як bash, і пишете команди з підручника з bash #!/bin/bash, не вказуйте заголовок #!/bin/sh, за винятком випадків, коли зазначено інше. Інакше ваші команди можуть вийти з ладу. І якщо ви ще не написали скрипт, викликайте його безпосередньо ( ./foo.sh, bar/foo.sh) замість того, щоб вгадувати оболонку ( sh foo.sh, sh bar/foo.sh). Шебанг повинен викликати потрібну оболонку.

І ось ще два види виклику:

cat foo.sh | dash
dash < foo.sh

5

.і sourceеквівалентні тим, що вони не породжують підпроцес, а виконують команди в поточній оболонці. Це важливо, коли сценарій встановлює змінні середовища або змінює поточну робочу директорію.

Використання шляху або надання його для /bin/shстворення нового процесу, в якому виконуються команди.


2
sh script
bash script

Я розмірковую, чи є більше ...

.і sourceоднакові. Після виконання будь-яких змін середовища в scriptзберігаються. Зазвичай його використовують для джерела бібліотеки Bash, тому бібліотеку можна повторно використовувати у багатьох різних сценаріях.

Також це хороший спосіб зберегти поточний каталог. Якщо ви зміните каталог у скрипті, він не буде застосований до оболонки, якою ви виконуєте цей сценарій. Але якщо ви запускаєте його для запуску, після закінчення сценарію збережеться поточний каталог.


2
.працює лише в ш / баш і споріднених оболонках. sourceтакож працює в csh і споріднених оболонках.
KeithB


1
. ./filename
# ( dot space dot slash filename )

Запускає скрипт у поточній оболонці, коли каталог не стоїть на шляху.


1

. і джерело трохи відрізняються в zsh принаймні (саме цим я користуюсь), тому що

source file

Працює, поки

. file

ні, це потрібно

. ./file
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.