Який випадок використання noop [:] у bash?


123

Я шукав noop у bash (:), але не зміг знайти жодної хорошої інформації. Яка точна ціль або спосіб використання цього оператора?

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

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Будь ласка, повідомте мене про будь-який випадок використання цього оператора в режимі реального часу або будь-яке місце, де його обов'язково використовувати.



Зауважте, що :вбудований існує в bourne shell і ksh, а також bash.
ghoti

Дивіться також :у tldp.org/LDP/abs/html/special-chars.html
choroba

6
Як це не справжнє запитання? Я думаю, що це дійсно гарне питання. Я навіть добре використовую це, але відповіді не можу.
Стівен Лу

Відповіді:


175

Це там більше з історичних причин. Товста кишка вбудована: точно рівнозначна true. Це традиційно використовувати, trueколи значення повернення важливе, наприклад, у нескінченному циклі:

while true; do
  echo 'Going on forever'
done

Це традиційно використовувати, :коли синтаксис оболонки вимагає команди, але вам нічого робити.

while keep_waiting; do
  : # busy-wait
done

В :дати BUILTIN всі шляхи назад до Thompson оболонки , це присутнє в Unix v6 . :був індикатором мітки для gotoзаяви оболонки Томпсона . Мітка може бути будь-яким текстом, тому :подвоюється як показник коментаря (якщо немає goto comment, то : commentце фактично коментар). Bourne оболонка була , gotoале продовжувала :.

Звичайна ідіома , яка використовує :це : ${var=VALUE}, який встановлює , varщоб , VALUEякщо це НЕ встановлено , і не робить нічого , якщо varвже було встановлено. Ця конструкція існує лише у формі підстановки змінної, і ця заміна змінної повинна якось бути частиною команди: команда без операції служить чудово.

Дивіться також Яким цілям служить вбудована кишка? .


11
Гарне резюме. Крім того, оригінальну оболонку Bourne не використовували #для коментарів (або #!/bin/shshebangs); :команда представила коментарі, і горе наївного програміста , який зробив хорошу коробку зірок в коментарях: : ****(або, що ще гірше, : * * *).
Джонатан Леффлер

3
@JonathanLeffler: Сором мені, але я цього не розумію. Що б сталося : * * *?
DevSolar

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

Я вважаю, що це може призвести до того, що ваш сценарій не вдасться, якщо вам трапляється запускати в каталог з великою кількістю файлів, змушуючи глобальне розширення перевищувати обмеження довжини команди? Можливо, тільки якщо у вас є set -e. У будь-якому випадку, сподіваємось, ми можемо просто погодитися використовувати #:)
Jack O'Connor

2
Я іноді використовую, while : ; do ... ; doneякщо хочу швидку і брудну нескінченну петлю. (Зазвичай sleepв циклі є а, і я набираю Ctrl-C, щоб його вбити.)
Кіт Томпсон,

21

Я використовую його, якщо заяви, коли я коментую весь код. Наприклад, у вас є тест:

if [ "$foo" != "1" ]
then
    echo Success
fi

але ви хочете тимчасово прокоментувати все, що міститься в:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Через що bash видає синтаксичну помилку:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash не може мати порожні блоки (WTF). Отже, ви додаєте неоперативний:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

або ви можете скористатися неоперативом, щоб прокоментувати рядки:

if [ "$foo" != "1" ]
then
    : echo Success
fi

12

Якщо ви використовуєте, set- eто || :це прекрасний спосіб не вийти зі скрипту, якщо стався збій (це явно змушує його пройти).


1
Я вважаю це корисним для певних програм для виклику оболонок скриптів. Наприклад, Automator на Mac вийде помилково і не скаже вам, чому. Але, використовуючи || :згодом самостійно обробляти помилку за допомогою exit 0заповіту, ви зможете відображати всі stdout та stderr у вікні програми під час налагодження. Поміркуйте, { foo ... 2>&1; } || :що набагато простіше, ніж налаштувати складніші пастки та театри.
Beejor

7

Ви б використовували :для подачі команди, яка вдається, але нічого не робить. У цьому прикладі команда «багатослів’я» вимикається за замовчуванням, встановивши її на :. Параметр 'v' включає його.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  

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

@qodeninja: Ніхто не стверджував, що призначення змінної "нічого не робило"; справа в тому, що $(verbosity $i)пізніше (і умовно) нічого не робить.
Гонки легкості по орбіті

6

Ігноруйте aliasаргументи

Деколи ви хочете мати псевдонім, який не бере жодних аргументів. Ви можете це зробити, використовуючи: :

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

На жаль, ця відповідь не демонструє, як :працює. Приклад, який ви подали, здається зайвим.
qodeninja

2
Питання не в тому, як це працює, а в тому, що використовується . Це правда, моя відповідь трохи схожа на stackoverflow.com/a/37170755/6320039 . Але це справді не той самий випадок використання. Я буду радий покращити свою відповідь, але я дійсно не знаю, як тут ..
Ulysse BN

1
Це трохи фанк - кутовий випадок, якщо ви хочете - але все-таки досить цікавий і міг би бути розумним трюком у задній кишені.
Майк S

2
Те саме можна досягти і з#
Зої Хьюлл

2
@ZoeyHewll, не зовсім. Оскільки псевдоніми замінюються текстово перед будь-яким видом виконання, використовуючи псевдонім, щоб #змусити ігнорувати все, що потрапляє після синтаксичного після того ж рядка. Це насправді інакше (розгляньте my_alias arg ; other_commandабо, навпаки, my_alias arg1 {BACKSLASH}{NEWLINE} arg2), і, мабуть, не те, що ви хочете, оскільки це може спричинити синтаксичні помилки (здогадайтесь, що while my_alias ; do true ; doneі що while true ; do my_alias ; doneбуде робити).
Maëlan

4

Одне використання - як багаторядкові коментарі, або коментування частини коду для тестування, використовуючи його разом із файлом тут.

: << 'EOF'

This part of the script is a commented out

EOF

Не забудьте використовувати лапки, EOFщоб будь-який код всередині не був оцінений, наприклад $(foo). Вона також може бути варто використовувати інтуїтивне ім'я термінатора , як NOTES, SCRATCHPADабо TODO.


Класна хитрість! Особливо для нотаток нуля та коду, що вимагає десяток символів "#", коли їх обкладено важко. Побічним ефектом є те, що на відміну від рядків коментарів, деякі підсвічувачі синтаксису ігнорують гередоки, забарвлюючи їх як звичайний код (або принаймні звичайний текст). Що може бути чудовим для вивчення коментованого коду або збереження очей від абзаців у кольорі неонових коментарів. Тільки застереження полягає в тому, що такі блоки слід зазначити як коментарі до спільного коду, оскільки синтаксис є унікальним і може спричинити плутанину. Знову ж таки, про це ми говоримо про оболонку, де потенціал плутанини є системним.
Beejor

3

Дві мої.

Вставити коментарі POD

Досить прикольне застосування :- для вбудовування коментарів POD у сценарії bash , завдяки чому довідкові сторінки можна швидко створювати. Звичайно, можна було б переписати весь сценарій на Perl ;-)

Прив'язка функції часу виконання

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

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

Ви отримуєте:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar

2

Іноді безвідмовні пропозиції можуть зробити ваш код більш читабельним.

Це може бути питанням думки, але ось приклад. Припустимо, ви створили функцію, яка працює, провівши два шляху Unix. Він обчислює "шлях зміни", необхідний для переходу з одного шляху на інший. Ви встановлюєте обмеження на свою функцію, що шляхи повинні обидва починатись з '/' АБО обидва не повинні.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Деякі розробники захочуть видалити неопераційний, але це означатиме заперечення умовного:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Тепер, на мій погляд, з пункту if якщо не так зрозуміло, в яких умовах ви хочете пропустити виконання функції. Щоб усунути неактивний параметр і зробити це чітко, ви хочете перенести if-функцію з функції:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

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

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


3
Якщо ви відповідаєте на запитання про мету :, вам слід скористатись :не trueу відповіді. Тим НЕ менше, найпростіший спосіб звести на немає умовних тут використовувати одну [[ ... ]]команду і префікс з !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
чепнер

1
Ах, дуже приємно, і я повинен проголосувати свою відповідь. Хммм .... Я все ще розмірковую про приклади, коли мені довелося використовувати неоперативний пункт. Це найкраще, що я придумав.
Bitdiot

Це дійсно просто збережено для зворотної сумісності, хоча : ${...=...}, мабуть, менш незручно, ніж true ${...=...}. Деякі можуть віддати перевагу while :; doі while true; doдля лаконічності.
чепнер

2

Дещо пов’язаний з цією відповіддю , я вважаю, що цей неоперативний варіант досить зручний для злому сценаріїв поліглотів . Наприклад, ось вагомий коментар як для bash, так і для vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

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


Що стосується того, чому хтось би зробив таку складну справу, як написання поліглот-скрипту (окрім того, що це круто): він виявляється корисним у ситуаціях, коли ми зазвичай записуємо кілька файлів скриптів на декількох різних мовах, при цьому файл Xпосилається на файл Y.

У такій ситуації поєднання обох сценаріїв в одному поліглот-файлі дозволяє уникнути будь-якої роботи Xщодо визначення шляху до Y(просто "$0"). Що ще важливіше, це робить зручніше пересуватися або розповсюджувати програму.

  • Загальний приклад. Існує добре відома давня проблема з shebangs: більшість систем (включаючи Linux та Cygwin) дозволяють передавати інтерпретатору лише один аргумент. Наступні shebang:

    #!/usr/bin/env interpreter --load-libA --load-libB

    запустить наступну команду:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    а не за призначенням:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Таким чином, ви закінчите писати скрипт для обгортки, наприклад:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Ось тут поліглосія виходить на сцену.

  • Більш конкретний приклад. Я одного разу написав баш сценарій, який, серед іншого, посилався на Віма. Мені потрібно було надати Vim додаткове налаштування, що можна зробити за допомогою опції --cmd "arbitrary vimscript command here". Однак ця настройка була суттєвою, так що вбудовувати її в рядок було б жахливо (якщо це можливо). Отже, кращим рішенням було записати його в extenso в якийсь файл конфігурації, а потім змусити Vim прочитати цей файл -S "/path/to/file". Отже, я опинився у файлі basg / vimscript поліглота.


1

Я також використовував у ньому сценарії для визначення змінних за замовчуванням.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

0

припустимо, у вас є команда, якою ви хочете скористатися успіхом іншого:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

але тепер ви хочете виконувати команди умовно, і ви хочете показати команди, які будуть виконуватись (в режимі «сухого виконання»):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

тому якщо встановити DEBUG та NOEXEC, друга команда ніколи не з’являється. це тому, що перша команда ніколи не виконується (оскільки NOEXEC не порожня), але оцінка цього факту дає вам повернення 1, що означає, що підпорядкована команда ніколи не виконує (але ви хочете, щоб це було, оскільки це сухий запуск). тож, щоб виправити це, ви можете скинути значення виходу, залишене на стеку, за допомогою noop:

[ -z "$NOEXEC" ] && $cmd || :
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.