Як викликати bash, запустити команди всередині нової оболонки, а потім повернути управління користувачеві?


86

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

Я намагався:

$ bash -lic "some_command"

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

Ще одна деталь, яка може вплинути на відповіді: якщо я зможу змусити це працювати, я буду використовувати його у своїх .bashrcпсевдонімах, тому бонусні бали за aliasреалізацію!


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

2
Я хочу мати можливість розмістити щось на зразок alias shortcut='bash -lic "some_command"'мого .bashrc, а потім запустити shortcutі мати нову оболонку з some_commandуже запущеною в ній.
Фелікс Сапареллі,

Відповіді:


63
bash --rcfile <(echo '. ~/.bashrc; some_command')

розподіляє створення тимчасових файлів. Питання на інших сайтах:


Я спробував зробити це в Windows 10 (намагаючись написати пакетний файл / сценарій "запуску"), і я отримуюThe system cannot find the file specified.
Doctor Blue

1
@Scott налагоджує це поетапно: 1) Чи cat ~/.bashrcпрацює? 2) Чи cat <(echo a)працює? 3) Чи bash --rcfile somefileпрацює?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@Scott ми щойно натрапили на цю проблему на WSL (запуск bash з командного файлу запуску). Нашим рішенням було уникнути деяких символів, щоб пакет міг це зрозуміти: bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)повинен запускатися з командного рядка Windows.
user2561747

Чи є спосіб зробити так, щоб це працювало з перемиканням користувачів, наприклад sudo bash --init-file <(echo "ls; pwd")або sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile


39

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

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

Поламане рішення : Найпростіша і очевидна спроба вирішити це:

bash -c 'XXXX ; bash'

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

Краще : спосіб обійти це динамічне створення файлу запуску та виклик bash за допомогою цього нового файлу ініціалізації, переконавшись, що ваш новий файл ініціювання викликає ваш звичайний, ~/.bashrcякщо це необхідно.

# Create a temporary file
TMPFILE=$(mktemp)

# Add stuff to the temporary file
echo "source ~/.bashrc" > $TMPFILE
echo "<other commands>" >> $TMPFILE
echo "rm -f $TMPFILE" >> $TMPFILE

# Start the new bash shell 
bash --rcfile $TMPFILE

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

Примітка: Я не впевнений, що / etc / bashrc зазвичай викликається як частина звичайної оболонки, що не входить в систему. Якщо так, ви можете захотіти отримати джерело / etc / bashrc, а також ваш ~/.bashrc.


Річ rm -f $TMPFILEце робить. Я насправді не пам’ятаю, як я використовував цей варіант, і зараз я використовую більш досконалі методи автоматизації, але це шлях!
Фелікс Сапареллі

7
Немає файлів tmp із заміною процесуbash --rcfile <(echo ". ~/.bashrc; a=b")
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
Тимчасовий файл буде очищений, лише якщо ваш сценарій Bash успішно працює. Більш надійним рішенням було б використання trap.
триплі

5

Ви можете перейти --rcfileдо Bash, щоб змусити його прочитати файл на ваш вибір. Цей файл буде прочитано замість вашого .bashrc. (Якщо це проблема, джерело ~/.bashrcз іншого сценарію.)

Редагувати: Отже, функція для запуску нової оболонки з матеріалу ~/.more.shбуде виглядати приблизно так:

more() { bash --rcfile ~/.more.sh ; }

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


3

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

$ cat script
cmd1
cmd2
$. сценарій
$ на даний момент cmd1 і cmd2 були запущені всередині цієї оболонки

Хм Це працює, але чи немає способу вбудувати все в файл alias=''?
Фелікс Сапареллі,

1
Ніколи не використовуйте псевдоніми; використовувати замість них функції. Ви можете поставити: 'foo () {cmd1; cmd2; } 'у вашому запусковому сценарії, а потім, виконуючи foo, буде виконано дві команди в поточній оболонці. Зверніть увагу, що жодне з цих рішень не дає вам нової оболонки, але ви можете виконати "exec bash", а потім "foo"
Вільям Персел

1
Ніколи не кажи ніколи. псевдоніми мають своє місце
glenn jackman

Чи є приклад варіанту використання, коли функцію не можна використовувати замість псевдоніма?
Вільям Перселл,

3

Додайте до ~/.bashrcтакого розділу:

if [ "$subshell" = 'true' ]
then
    # commands to execute only on a subshell
    date
fi
alias sub='subshell=true bash'

Тоді ви можете розпочати під оболонку з sub.


Бали за оригінальність. Сам псевдонім, мабуть, кращий, оскільки env subshell=true bash(оскільки це не витікає $subshellдля наступних команд): Використання env проти використання експорту .
Фелікс Сапареллі

Ти правий. Але я помітив, що це працює і без цього env. Для перевірки, визначено чи ні, я використовую echo $subshell(замість того, env | grep subshellщо ви використовуєте).
dashohoxha

3

Прийнята відповідь справді корисна! Просто додамо, що заміна процесу (тобто, <(COMMAND)) не підтримується в деяких оболонках (наприклад, dash).

У моєму випадку я намагався створити власну дію (в основному однорядковий скрипт оболонки) у менеджері файлів Thunar, щоб запустити оболонку та активувати вибране віртуальне середовище Python. Моєю першою спробою було:

urxvt -e bash --rcfile <(echo ". $HOME/.bashrc; . %f/bin/activate;")

де %fшлях до віртуального середовища, яким обробляє Thunar. Я отримав помилку (запустивши Thunar з командного рядка):

/bin/sh: 1: Syntax error: "(" unexpected

Потім я зрозумів, що мій sh(по суті dash) не підтримує заміну процесу.

Моє рішення було звернутися bashдо верхнього рівня для інтерпретації заміни процесу за рахунок додаткового рівня оболонки:

bash -c 'urxvt -e bash --rcfile <(echo "source $HOME/.bashrc; source %f/bin/activate;")'

Як варіант, я намагався використати тут-документ для, dashале без успіху. Щось на зразок:

echo -e " <<EOF\n. $HOME/.bashrc; . %f/bin/activate;\nEOF\n" | xargs -0 urxvt -e bash --rcfile

PS: У мене недостатньо репутації для розміщення коментарів, модератори, будь ласка, переносьте їх до коментарів або видаляйте, якщо це не допоможе з цим питанням.


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

2

Відповідно до відповіді daveraja , ось скрипт bash, який вирішить мету.

Розглянемо ситуацію, якщо ви використовуєте C-оболонку і хочете виконати команду, не виходячи з контексту / вікна C-оболонки наступним чином,

Команда, яку потрібно виконати : Шукати точне слово "Тестування" у поточному каталозі рекурсивно лише у файлах * .h, * .c

grep -nrs --color -w --include="*.{h,c}" Testing ./

Рішення 1 : Увійдіть у bash із C-оболонки та виконайте команду

bash
grep -nrs --color -w --include="*.{h,c}" Testing ./
exit

Рішення 2. Запишіть призначену команду в текстовий файл і виконайте її за допомогою bash

echo 'grep -nrs --color -w --include="*.{h,c}" Testing ./' > tmp_file.txt
bash tmp_file.txt

Рішення 3 : Запустіть команду в тому ж рядку, використовуючи bash

bash -c 'grep -nrs --color -w --include="*.{h,c}" Testing ./'

Рішення 4 : Створіть sciprt (одноразовий) і використовуйте його для всіх майбутніх команд

alias ebash './execute_command_on_bash.sh'
ebash grep -nrs --color -w --include="*.{h,c}" Testing ./

Сценарій такий,

#!/bin/bash
# =========================================================================
# References:
# https://stackoverflow.com/a/13343457/5409274
# https://stackoverflow.com/a/26733366/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://www.linuxquestions.org/questions/other-%2Anix-55/how-can-i-run-a-command-on-another-shell-without-changing-the-current-shell-794580/
# https://www.tldp.org/LDP/abs/html/internalvariables.html
# https://stackoverflow.com/a/4277753/5409274
# =========================================================================

# Enable following line to see the script commands
# getting printing along with their execution. This will help for debugging.
#set -o verbose

E_BADARGS=85

if [ ! -n "$1" ]
then
  echo "Usage: `basename $0` grep -nrs --color -w --include=\"*.{h,c}\" Testing ."
  echo "Usage: `basename $0` find . -name \"*.txt\""
  exit $E_BADARGS
fi  

# Create a temporary file
TMPFILE=$(mktemp)

# Add stuff to the temporary file
#echo "echo Hello World...." >> $TMPFILE

#initialize the variable that will contain the whole argument string
argList=""
#iterate on each argument
for arg in "$@"
do
  #if an argument contains a white space, enclose it in double quotes and append to the list
  #otherwise simply append the argument to the list
  if echo $arg | grep -q " "; then
   argList="$argList \"$arg\""
  else
   argList="$argList $arg"
  fi
done

#remove a possible trailing space at the beginning of the list
argList=$(echo $argList | sed 's/^ *//')

# Echoing the command to be executed to tmp file
echo "$argList" >> $TMPFILE

# Note: This should be your last command
# Important last command which deletes the tmp file
last_command="rm -f $TMPFILE"
echo "$last_command" >> $TMPFILE

#echo "---------------------------------------------"
#echo "TMPFILE is $TMPFILE as follows"
#cat $TMPFILE
#echo "---------------------------------------------"

check_for_last_line=$(tail -n 1 $TMPFILE | grep -o "$last_command")
#echo $check_for_last_line

#if tail -n 1 $TMPFILE | grep -o "$last_command"
if [ "$check_for_last_line" == "$last_command" ]
then
  #echo "Okay..."
  bash $TMPFILE
  exit 0
else
  echo "Something is wrong"
  echo "Last command in your tmp file should be removing itself"
  echo "Aborting the process"
  exit 1
fi

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

Позначено як "не відповісти", оскільки це не спроба відповісти на це запитання (хоча це справді велика спроба відповісти на інше!)
Чарльз Даффі,

0

Ось ще один (робочий) варіант:

Це відкриває новий термінал gnome, а потім у новому терміналі працює bash. Спочатку читається rc-файл користувача, потім команда ls -laнадсилається для виконання до нової оболонки, перш ніж вона стає інтерактивною. Останнє відлуння додає додатковий новий рядок, необхідний для завершення виконання.

gnome-terminal -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

Я також вважаю корисним декору прикрасити термінал, наприклад кольором для кращої орієнтації.

gnome-terminal --profile green -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

-1

Виконання команд у фоновій оболонці

Просто додайте &в кінець команди, наприклад:

bash -c some_command && another_command &

-1
$ bash --init-file <(echo 'some_command')
$ bash --rcfile <(echo 'some_command')

Якщо ви не можете або не хочете використовувати заміну процесу:

$ cat script
some_command
$ bash --init-file script

Інший спосіб:

$ bash -c 'some_command; exec bash'
$ sh -c 'some_command; exec sh'

sh-тільки способом ( dash, busybox):

$ ENV=script sh

Хороші альтернативи, але було б корисно ENV=scriptвидалити або переформатувати верхній розділ (перед частиною), щоб уникнути плутанини з відвертою копіювальною вставкою верхньої відповіді.
Фелікс Сапареллі,

@ FélixSaparelli Відверто кажучи, всі рішення , але ENV+ shодна присутня в інших відповідях, але в основному похований в тоннах тексту або в оточенні непотрібної / несуттєвою кодою. Я вважаю, що коротке, чітке резюме було б корисним для всіх.
x-yuri
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.