Чому "CD" не працює в сценарії оболонки?


766

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

#!/bin/bash
cd /home/tree/projects/java

Я зберег цей файл як proj, додав дозвіл на виконання chmodта скопіював його /usr/bin. Коли я називаю це: projвін нічого не робить. Що я роблю неправильно?


10
дублікат крос-сайту: superuser.com/questions/176783/…
lesmana

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

5
@lesmana як це дублікат?
аланд

@aland Оскільки OP насправді не запускає сценарій, тому робочий директор не змінюється для нього. cdкоманда добре працює всередині скриптів, спробуйте самі.
Олександр Гончій

Відповіді:


634

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

Один із способів подолати це - замість цього використовувати псевдонім:

alias proj="cd /home/tree/projects/java"

7
Псевдоніми не такі гнучкі в управлінні або зміні. У випадку, коли багато CD-дисків, сценарії можуть бути кращими.
Thevs

16
Функції більш гнучкі, ніж псевдоніми, тож саме там ви б подивилися далі, коли псевдонімів недостатньо.
ефемія

4
Чи варто зауважити, що в MS-DOS поведінка скриптів полягала в тому, що викликаний скрипт може змінити каталог (і навіть диск) оболонки командної виклику? І що у Unix немає цього дефекту?
Джонатан Леффлер

23
Джонатан: хоча це правда, це питання насправді не пов'язане з цим питанням. Відповіді на SO отримали б вдвічі довше, якби вони мали перераховувати відповідні недоліки в MS-DOS!
Грег Хьюгілл

17
Ще один спосіб обійти це джерело файлу сценарію: . my-scriptабо source my-script.
HelloGoodbye

503

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

Ви можете запустити скрипт у вашому поточному процесі за допомогою команди "крапка":

. proj

Але я вважаю за краще пропозиція Грега використовувати псевдонім у цьому простому випадку.


95
.також написано source, виберіть те, що вам запам'ятовується.
ефемія

47
@Ephemient: Хороший ресурс, що пояснює, чому це робочий ресурс Також доводить, що лінь - не необхідність - часто є матерією джерела винаходів
Адам Лісс

8
@ephemient: зауважте, що джерело використовується в оболонці C та Bash; він не підтримується ні в оболонках POSIX, ні в Korn, ні в класичній оболонці Bourne.
Джонатан Леффлер

2
"Джерело" використовується в Z-оболонці
Натан Моос

1
@AdamLiss Це чудово працює, але є один недолік - якось команда source / dot відключає можливість автоматичного завершення назви сценарію / команди за допомогою клавіші TAB. Можна додати аргументи / введення за допомогою клавіші TAB, але, на жаль, ім'я команди потрібно вводити безпосередньо. Чи знаєте ви, як зробити так, щоб ключ TAB працював у цьому випадку?
Рафал

215

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

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

jhome () {
  cd /home/tree/projects/java
}

Ви можете просто ввести це або помістити його в один з різних файлів запуску оболонки.


6
Я згоден, це найкраща відповідь, зіткнувся. Крім того, псевдонім може бути доречним у деяких ситуаціях, але якщо він намагається використовувати CD у скрипті і хотів до нього додати що-небудь інше, псевдонім буде марним.
Джастін Бусер

4
Це виглядає як рішення! І я намагаюся його реалізувати, але це не виконує :( ось мій сценарій: #! / Bin / bash jhome () {echo "будь ласка" cd / home echo "work"} jhome
Gant

1
@GantMan, ви повинні додати це у "файли запуску оболонки", як ~ / .bashrc
Jackie Yeh

3
Ви також можете використовувати аргумент (або два ...), тобто:jhome(){ cd /home/tree/projects/$1; }
irbanana

1
Це має бути правильним способом, це дає можливість використовувати спеціальні символи, наприклад "-".
bangkokguy

159

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

./myscript.sh

робити

. ./myscript.sh

(Зверніть увагу на крапку та пробіл перед назвою сценарію.)


2
Це круто і, напевно, добре знати. Що саме робить останній (як це працює)? Це, мабуть, найкраще рішення для цієї теми.
Йона

2
fwiw, в розділі коментарів відповіді Адама Лісса, ефеміент відповідає на питання, що таке ". є. Це те саме, щоsource
g19fanatic

1
Будьте обережні, "джерело" - це башизм. тобто тире (за замовчуванням Debian для / bin / sh) не підтримує його, поки ". працює як очікувалося.
алло

Чудова відповідь! також, якщо myscript.shвін знаходиться в каталозі, включеному в $ PATH, ви можете його джерело з будь-якого місця, не вказуючи повний шлях.
CodeBrew

Це працювало для мене найкраще. Це рішення є найпростішим (+1).
HuserB1989

96

Щоб створити bash-скрипт, який буде записаний у вибраний каталог:

Створіть файл сценарію

#! / бін / ш
# файл: / script / cdjava
#
cd / home / askgelal / проекти / java

Потім створіть псевдонім у файлі запуску.

#! / бін / ш
# файл /scripts/mastercode.sh
#
псевдонім cdjava = '. / script / cdjava '

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

Наприклад, створіть головний файл псевдонімів / функцій: /scripts/mastercode.sh
(Поставте псевдонім у цей файл.)

Потім в кінці файлу .bashrc :

source /scripts/mastercode.sh



Тепер його легко записати у свій каталог Java, просто введіть cdjava і ви там.


2
файл mastercode.shне потребує shabang ( #!/bin/sh), оскільки він не (і не може бути) виконаний у підзарядці. Але в той же час вам потрібно документувати "аромат" оболонки цього файлу; наприклад, ksh або bash (або (t) csh / zsh тощо), і це майже точно не є насправді sh. Я зазвичай додаю коментар (але не шебанг), щоб повідомити про це; наприклад, "цей файл призначений для отримання (з bash), а не запускається як сценарій оболонки."
michael

Ще одна порада (для bash): якщо ви використовуєте змінні в своєму скрипті, тоді, коли сценарій sourced (через alias), ці змінні будуть просочуватися у ваше середовище оболонки. Щоб уникнути цього, виконайте всю роботу сценарію у функції, а просто зателефонуйте в кінці сценарію. У межах функції оголосити будь-які змінні local.
Ревінь

в ubuntu просто створіть ~ / .bash_aliases (якщо він ще не існує). Потім просто додайте туди свої псевдоніми, перезавантажте термінал і завершене.
Влад

51

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

. script_name

або, альтернативно, його більш читабельний, але специфічний псевдонім source:

source script_name

Це дозволяє уникнути додаткової cdоболонки та дозволяє будь-яким змінним чи вбудованим (у тому числі ) впливати на поточну оболонку.


38

Ідея Джеремі Рутена про використання симпосилання викликала думку, яка не перетнула жодної іншої відповіді. Використання:

CDPATH=:$HOME/projects

Провідна кишка важлива; це означає, що якщо в поточному каталозі є каталог "dir", то " cd dir" зміниться на це, а не скачучи десь в іншому місці. З набором значень, як показано, ви можете:

cd java

і, якщо в поточному каталозі немає підкаталога під назвою java, то він перенесе вас безпосередньо до $ HOME / Projects / java - ні псевдонімів, ні скриптів, ні сумнівних виконавців чи команд з крапками.

Мій $ HOME - / Користувачі / jleffler; мій $ CDPATH:

:/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work

30

Використовуйте exec bashв кінці

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

Однак це запитання часто задається, оскільки хочеться залишитись у (новому) bash-рядку в певному каталозі після виконання bash-скрипту з іншого каталогу.

Якщо це так, просто виконайте дочірній екземпляр bash в кінці сценарію:

#!/usr/bin/env bash
cd /home/tree/projects/java
exec bash

16
Це створює нову підшару. Після введення exitви повернетесь до оболонки, де ви запустили цей сценарій.
трійка

1
Коли сценарій викликався кілька разів, здається, він створює вкладені оболонки, і мені потрібно вводити "вихід" багато разів. Чи можна це якось вирішити?
ВладСавицький

Дивіться інші відповіді: використовуйте псевдонім, використовуйте "pushd / popd / dirs" або використовуйте "source"
qneill

25

Я змусив свій код працювати, використовуючи. <your file name>

./<your file name> доза не працює, оскільки вона не змінює ваш каталог у терміналі, вона просто змінює каталог, специфічний для цього сценарію.

Ось моя програма

#!/bin/bash 
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace

Ось мій термінал

nova:~ Kael$ 
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$ 

2
Яка різниця між . somethingта ./something?? Ця відповідь спрацювала для мене, і я не розумію, чому.
Дракорат

. somethingдозволяє запускати скрипт з будь-якого місця, ./somethingвимагає , щоб ви
знаходилися


Чи можете ви поставити крапку в лінії шебанга? #!. /bin/bash?
Зігфрід


12

Коли ви запускаєте скрипт оболонки, він запускає новий екземпляр цієї оболонки ( /bin/bash). Таким чином, ваш сценарій просто запускає оболонку, змінює каталог і виходить. По-іншому, cd(та інші такі команди) в скрипті оболонки не впливають і не мають доступу до оболонки, з якої вони були запущені.


11

У моєму конкретному випадку мені потрібно було занадто багато разів, щоб змінити один і той же каталог. Отже, на свій .bashrc (я використовую ubuntu) я додав

1 -

$ nano ~. / bashrc

 function switchp
 {
    cd /home/tree/projects/$1
 }

2-

$ source ~ / .bashrc

3 -

$ switchp java

Безпосередньо це зробить: cd / home / tree / Проекти / java

Сподіваюся, що це допомагає!


1
@WM Ви можете з часом зробити "$ switchp" без жодного параметра, щоб перейти безпосередньо до дерева проектів
workdreamer

2
@workdreamer Функціональний підхід, який ви описали вище, вирішив тут невелику проблему при вході в підпапки, які мають ту саму батьківську папку в моїй системі. Дякую.
WM

10

Ви можете зробити наступне:

#!/bin/bash
cd /your/project/directory
# start another shell and replacing the current
exec /bin/bash

EDIT: Це може бути "пунктирним", щоб запобігти створенню наступних оболонок.

Приклад:

. ./previous_script  (with or without the first line)

1
Це стає досить безладним після декількох запусків. Вам доведеться exit(або ctrl + d) кілька разів вийти з оболонки, наприклад. Псевдонім настільки чистіший (навіть якщо команда оболонки видає каталог, і це CD до виводу - псевдонім щось = "cd getnewdirectory.sh")
dbr

Зверніть увагу на "exec". Це дозволяє замінити стару оболонку.
Thevs

2
Exec замінює лише підзагін, який виконував команду cd, а не оболонку, яка запускала сценарій. Якби ви поставили крапку в сценарії, то ви були б правильні.
Джонатан Леффлер

Зробити це «пунктирно» - не проблема. Я щойно запропонував рішення. Це не залежить від того, як ви запускаєте це.
Thevs

2
Хе-хе, я думаю, що краще просто "розставити" однорядковий сценарій із лише командою cd :) Я все одно
збережу

7

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

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


Наявність цього посилання у кожному каталозі буде неприємністю. Можна було б покласти симпосилання в $ HOME, а потім зробити 'cd ~ / my-project'. Відверто кажучи, простіше використовувати CDPATH.
Джонатан Леффлер

7

Ви можете комбінувати псевдонім Адама та Грега та крапки підходів, щоб зробити щось, що може бути більш динамічним -

alias project=". project"

Тепер запущений псевдонім проекту буде виконувати сценарій проекту в поточній оболонці на відміну від нижньої частини.


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

7

Ви можете комбінувати псевдонім і сценарій,

alias proj="cd \`/usr/bin/proj !*\`"

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

Наприклад, ваш сценарій може бути

#!/bin/bash
echo /home/askgelal/projects/java/$1

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


3
чому? ви можете просто використовувати: proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(або, proj "foo bar"<= у випадку, якщо у вас є пробіли) ... або навіть (наприклад): proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=> робить cdв/home/user/projects/java/a/b/c
michael


5

У вашому файлі ~ / .bash_profile. додати наступну функцію

move_me() {
    cd ~/path/to/dest
}

Перезавантажте термінал, і ви можете ввести

move_me 

і ви будете переміщені до папки призначення.


3

Ви можете скористатися оператором &&:

компакт-диск myDirectory && ls


Downvote: Тут не намагаються відповісти на фактичне запитання. Ви можете запустити дві команди підказки ( &&якщо ви хочете, щоб друга була умовною для першого, або просто з ;новою лінією або між ними), але введення цього сценарію поверне вас до "чому не використовується батьківська оболонка новий каталог, коли cdсправді було успішно? "
tripleee

3

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

Інший спосіб зробити це - реалізувати свій скрипт як функцію в bash.

function cdbm() {
  cd whereever_you_want_to_go
  echo "Arguments to the functions were $1, $2, ..."
}

Ця методика використовується autojump: http://github.com/joelthelion/autojump/wiki, щоб забезпечити вам вивчення закладок каталогів оболонок.


3

Ви можете створити функцію, як нижче, у вашому, .bash_profileі вона буде працювати безперебійно.

Наступна функція приймає необов'язковий параметр, який є проектом. Наприклад, можна просто запустити

cdproj

або

cdproj project_name

Ось визначення функції.

cdproj(){
    dir=/Users/yourname/projects
    if [ "$1" ]; then
      cd "${dir}/${1}"
    else
      cd "${dir}"
    fi
}

Не забудьте джерело свого .bash_profile


1
набагато краща відповідь, ніж псевдонім
Pax0r

Це рішення геніальне своєю простотою та гнучкістю.
Kjartan

3

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

#!/bin/bash

# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash

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

root@intel-corei7-64:~# ./mov_dir.sh

root@intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root@intel-corei7-64:~#

Це навіть поверне вас до початкового каталогу при виході ( CTRL+ d)


3
Нерест нового баша означає, що ви втратите свою історію, і вона почне свіжою.
Тиш

2

LOOOOНа наступний час, але я зробив наступне:

створити файл, який називається регістр

вставте у файл наступне:

#!/bin/sh

cd /home/"$1"

збережіть його, а потім:

chmod +x case

Я також створив псевдонім у своєму .bashrc:

alias disk='cd /home/; . case'

тепер, коли я набираю:

case 12345

по суті я набираю:

cd /home/12345

Ви можете ввести будь-яку папку після "case":

case 12

case 15

case 17

це як набирати текст:

cd /home/12

cd /home/15

cd /home/17

відповідно

У моєму випадку шлях значно довший - ці хлопці підсумували його з ~ info раніше.


1
cdВ псевдонім є зайвим і позбавленим смаку; псевдонім повинен просто '. ~ / case` замість цього. Також caseє застереженим ключовим словом, тому досить поганий вибір імені.
tripleee

1

Вам не потрібен сценарій, лише встановіть правильний параметр і створіть змінну середовища.

shopt -s cdable_vars

у вашому ~/.bashrcдозволяє до cdзмісту змінних середовища.

Створіть таку змінну середовища:

export myjava="/home/tree/projects/java"

і ви можете використовувати:

cd myjava

Інші альтернативи .


0

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

function proj
   cd /home/tree/projects/java
end
funcsave proj

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

Якщо ви хочете, ви можете вручну додати функціональний файл, виконавши наступне:

nano ~/.config/fish/functions/proj.fish

і введіть текст:

function proj
   cd /home/tree/projects/java
end

і, нарешті, натисніть ctrl + x для виходу та y, а потім повернення, щоб зберегти зміни.

( ПРИМІТКА. Перший спосіб використання функції funcsave створює для вас файл proj.fish ).


0

У мене є простий скрипт bash під назвою p, щоб керувати зміною каталогу на
github.com/godzilla/bash-stuff
просто помістити сценарій у ваш локальний каталог бін (/ usr / local / bin)
і поставити

alias p='. p'

у вашому .bashrc


що це робить? схоже, він би рекурсивно запускався сам, хоча я впевнений, якщо ти
Райан Тейлор

0

Зверніть увагу на обговорення Як встановити робочий каталог батьківського процесу?

Він містить кілька хакейних відповідей, наприклад, https://stackoverflow.com/a/2375174/755804 (змінивши батьківський каталог процесів через gdb, не робіть цього) та https://stackoverflow.com/a/51985735/755804 ( команда, tailcdяка вводить cd dirname у вхідний потік батьківського процесу; ну, в ідеалі це має бути частиною bash, а не хаком)


0

Це старе питання, але я дуже здивований, що не бачу тут цього фокусу

Замість використання CD ви можете використовувати

export PWD=the/path/you/want

Не потрібно створювати підзаголовки або використовувати псевдоніми.

Зауважте, що ви відповідаєте за те, щоб переконатися, що / шлях / ви / хочете, існує.


На жаль, не вийшло.
ttfreeman

0

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

Одне рішення - використовувати функції bash замість bash script ( sh); помістивши ваш код скрипта bash у функцію. Це робить функцію доступною як команда, і тоді вона буде виконуватися без дочірнього процесу, а отже, будь-якогоcd команда вплине на оболонку абонента.

Функції Bash:

Однією з особливостей профілю bash є зберігання спеціальних функцій, які можна запустити в терміналі або в bash-скриптах так само, як ви запускаєте додаток / команди, це також може бути використане як ярлик для довгих команд.

Щоб широко функціонувати в системі, вам потрібно буде скопіювати її в кінці декількох файлів

/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile

Ти можеш sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile швидко редагувати / створювати ці файли

Як :

Скопіюйте код скрипта bash всередині нової функції в кінці файлу профілю bash та перезапустіть термінал, потім можна запустити cdd або будь-яку функцію, яку ви написали.

Приклад сценарію

Створення ярлика для cd ..зcdd

cdd() {
  cd ..
}

я ярлик

ll() {
  ls -l -h
}

я ярлик

lll() {
  ls -l -h -a
}

-2

Ви можете виконати деякі рядки в одній підкалі, якщо ви закінчите рядки з косою рисою.

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