Які відмінності між виконанням скриптів оболонки за допомогою "вихідного файлу.sh", "./file.sh", "sh file.sh", ". ./file.sh ”?


13

Подивіться на код:

#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
        read -p "Enter UID:  " uid
        logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
        read -p "Enter Logname:  " logname
fi
not=`ps -au$logname | grep -c bash`
echo  "The number of terminals opened by $logname are $not"

Цей код використовується для з'ясування кількості терміналів, відкритих користувачем на одному ПК. Зараз увійшли два користувачі, скажімо, x і y. На даний момент я зареєстрований як y, а у користувача x відкриті 3 термінали. Якщо я виконую цей код y, використовуючи різні способи, як було зазначено вище, результати:

$ ./file.sh
The number of terminals opened by x are 3

$ bash file.sh
The number of terminals opened by x are 5

$ sh file.sh
The number of terminals opened by x are 3

$ source file.sh
The number of terminals opened by x are 4

$ . ./file.sh
The number of terminals opened by x are 4

Примітка. Я передав 1 і uid 1000 для всіх цих виконуваних файлів.

Тепер ви можете, будь ласка, пояснити відмінності між усім цим?


різниця полягає в тому, яка оболонка виконується. sh not bash
j0h

2
Два останніх страти також відрізняються тим, що ви виконуєте в одному контексті. Більше тут
Zaka Elab

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

@RamanaReddy інший користувач, можливо, запустив сценарій або запустив нову вкладку. Хто знає?
muru

Відповіді:


21

Єдина основна відмінність - між джерелом пошуку та виконанням сценарію. source foo.shбуде джерело його, і всі інші приклади, які ви показуєте, виконуються. Більш детально:

  1. ./file.sh

    Це виконає сценарій під назвою, file.shякий знаходиться в поточному каталозі ( ./). Зазвичай під час запуску commandоболонка буде шукати каталоги у вашому $PATHфайлі для виконуваного файлу, який називається command. Якщо ви надаєте повний шлях, наприклад, /usr/bin/commandабо ./command, тоді $PATHігнорується і цей конкретний файл виконується.

  2. ../file.sh

    Це в основному те саме, що ./file.shза винятком того, що замість того, щоб шукати в поточному каталозі для file.sh, він шукає в батьківському каталозі ( ../).

  3. sh file.sh

    Цей еквівалент sh ./file.sh, як вище, запустить скрипт, який називається file.shв поточному каталозі. Різниця полягає в тому, що ви явно запускаєте його з shоболонкою. У системах Ubuntu це є dashі ні bash. Зазвичай у скриптів є рядок shebang, який дає програмі, яку вони повинні запускати як. Викликання їх за допомогою іншого відрізняється від цього. Наприклад:

    $ cat foo.sh
    #!/bin/bash  
    ## The above is the shebang line, it points to bash
    ps h -p $$ -o args='' | cut -f1 -d' '  ## This will print the name of the shell

    Цей сценарій просто надрукує ім'я оболонки, яка використовується для його запуску. Давайте подивимося, що воно повертається, коли викликається різними способами:

    $ bash foo.sh
    bash
    $ sh foo.sh 
    sh
    $ zsh foo.sh
    zsh

    Таким чином, виклик виклику скрипту за допомогою shell scriptперекриє лінію shebang (якщо вона присутня) та запустить сценарій будь-якою оболонкою.

  4. source file.sh або . file.sh

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

    $ cat foo.sh
    #!/bin/bash
    foo="Script"
    echo "Foo (script) is $foo"

    Тепер, якщо я встановив змінну fooна щось інше в батьківській оболонці і потім запустив сценарій, сценарій буде надрукувати інше значення foo(тому що воно також встановлено в сценарії), але значення fooв батьківській оболонці буде незмінним:

    $ foo="Parent"
    $ bash foo.sh 
    Foo (script) is Script  ## This is the value from the script's shell
    $ echo "$foo"          
    Parent                  ## The value in the parent shell is unchanged

    Однак, якщо я джерело сценарію замість його виконання, він буде запущений у тій же оболонці, щоб значення fooв батьківській програмі було змінено:

    $ source ./foo.sh 
    Foo (script) is Script   ## The script's foo
    $ echo "$foo" 
    Script                   ## Because the script was sourced, 
                             ## the value in the parent shell has changed

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


Зважаючи на це, причина, коли ви отримуєте різні відповіді, - це, перш за все, у тому, що ваш сценарій не робить те, що ви думаєте, що робить. Він підраховує кількість разів, що bashз’являється у висновку ps. Це не кількість відкритих терміналів , це кількість запущених оболонок (насправді це навіть не так, але це вже інша дискусія). Для уточнення я трохи спростив ваш сценарій до цього:

#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo  "The number of shells opened by $logname is $not"

І запускати його різними способами, відкривши лише один термінал:

  1. Прямий запуск, ./foo.sh.

    $ ./foo.sh
    The number of shells opened by terdon is 1

    Тут ви використовуєте лінію shebang. Це означає, що сценарій виконується безпосередньо тим, що там встановлено. Це впливає на те, як скрипт відображається на виході ps. Замість того, щоб бути вказаними як bash foo.sh, це буде показано лише як foo.shце означає, що ваш grepпропустить це. Насправді запущені 3 екземпляри bash: батьківський процес, bash запуск сценарію та ще один, який запускає psкоманду . Останнє важливо: запуск команди з заміною команд ( `command`або $(command)) призводить до запуску копії батьківської оболонки, яка запускає команду. Однак тут жодне з них не показано через спосіб, який psпоказує його вихід.

  2. Прямий запуск з явною (bash) оболонкою

    $ bash foo.sh 
    The number of shells opened by terdon is 3

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

  3. Прямий запуск з іншою оболонкою ( sh)

    $ sh foo.sh
    The number of shells opened by terdon is 1

    Це відрізняється тим, що ви запускаєте сценарій з, shа не bash. Тому єдиним bashекземпляром є батьківська оболонка, де ви запустили свій скрипт. Усі інші снаряди, згадані вище, shзамість них управляються .

  4. Sourcing (або .чи source, то ж саме)

    $ . ./foo.sh 
    The number of shells opened by terdon is 2

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


На завершення, правильний підрахунок запущених процесів - це не розбір, psа використання pgrep. Всіх цих проблем можна було б уникнути, якби ви тільки бігли

pgrep -cu terdon bash

Отже, робоча версія вашого сценарію, яка завжди друкує потрібне число, (зауважте, відсутність підстановки команд):

#!/usr/bin/env bash
user="terdon"

printf "Open shells:"
pgrep -cu "$user" bash

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


Коли ви говорите, що заміна команд запускає копію батьківської оболонки, чим ця копія відрізняється від підрозділу, як, наприклад, під час запуску сценарію ./foo.sh?
Дідьє А.

І коли ви запускаєте pgrep без підстановки команд, я припускаю, що він працює з тієї ж оболонки, в якій працює сценарій? Так схожий на джерело?
Дідьє А.

@didibus Я не впевнений, що ти маєш на увазі. Підстановка команд працює в нижній частині; ./foo.shпрацює в новій оболонці, яка не є копією батьківського. Наприклад, якщо ви встановите foo="bar"у своєму терміналі, а потім запустите скрипт, який виконується echo $foo, ви отримаєте порожній рядок, оскільки оболонка сценарію не успадкувала значення змінної. pgrepце окремий двійковий файл, і так, він працює за сценарієм, який ви виконуєте.
тердон

В основному мені потрібно уточнення щодо: "відзначте відсутність заміни команд". Чому запуск бінарного файлу pgrep зі скрипту не додає додаткової оболонки, а запуск бінарного ps із заміною команд робить? По-друге, мені потрібно роз'яснення щодо "копії батьківської оболонки", це схоже на під-оболонку, де змінні оболонки батьківського копіювання копіюються на дитину? Чому підміна команд робить це?
Дідьє А.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.