Почему «cd» не работает в сценарии оболочки?

767

Я пытаюсь написать небольшой скрипт, чтобы изменить текущий каталог на каталог моего проекта:

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

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

ashokgelal
источник
10
дубликат кросс-сайта: superuser.com/questions/176783/…
lesmana
В будущем вы всегда можете попробовать проверить его pwdна последней строке. Поэтому, прежде чем сценарий закончится, вы можете проверить, работает он или нет ..
sobi3ch
5
@lesmana как это дубликат?
Аландские
@aland Поскольку OP фактически не запускает скрипт, поэтому рабочий каталог для него не меняется. cdКоманда хорошо работает внутри скриптов, попробуйте сами.
Александр Гончий

Ответы:

635

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

Один из способов обойти это, вместо этого использовать псевдоним:

alias proj="cd /home/tree/projects/java"
Грег Хьюгилл
источник
7
Псевдонимы не настолько гибки в управлении или изменении. В случае многих компакт-дисков, сценарии могут быть лучше.
Thevs
16
Функции более гибкие, чем псевдонимы, поэтому вы можете посмотреть дальше, когда псевдонимов недостаточно.
Эфимент
4
Стоит ли отмечать, что в MS-DOS поведение сценариев заключалось в том, что вызываемый сценарий мог изменить каталог (и даже диск) вызывающей командной оболочки? И что Unix не имеет этого дефекта?
Джонатан Леффлер
23
Джонатан: хотя это правда, на самом деле это не связано с вопросом. Ответы на SO получили бы вдвое больше, если бы им приходилось перечислять соответствующие недостатки в MS-DOS!
Грег Хьюгилл
17
Другой способ обойти это - получить файл сценария: . my-scriptили source my-script.
Привет, до свидания,
505

Вы не делаете ничего плохого! Вы изменили каталог, но только внутри подоболочки, которая запускает скрипт.

Вы можете запустить скрипт в текущем процессе с помощью команды "точка":

. 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
}

Вы можете просто напечатать это или поместить в один из различных файлов запуска оболочки.

DigitalRoss
источник
6
Я согласен, это лучший ответ, столкнулся. Кроме того, псевдоним может быть подходящим в некоторых ситуациях, но если он пытается использовать cd в скрипте и хочет добавить что-либо еще к нему, псевдоним будет бесполезен.
Джастин Бузер
4
Это похоже на решение! И я пытаюсь реализовать его, но он не работает :( вот мой сценарий: #! / Bin / bash jhome () {echo "пожалуйста" cd / home echo "работа"} jhome
Gant Laborde
1
@GantMan, вы должны добавить это в «файлы запуска оболочки», например ~ / .bashrc
Jackie Yeh
3
Вы также можете использовать аргумент (или два ...), то есть:jhome(){ cd /home/tree/projects/$1; }
irbanana
1
Это должно быть правильно, это позволяет использовать специальные символы, такие как «-», например.
Bangkokguy
159

Это cdделается в оболочке скрипта. Когда сценарий заканчивается, оболочка завершается, и вы остаетесь в том каталоге, в котором находились. «Исходный» сценарий, не запускайте его. Вместо:

./myscript.sh

делать

. ./myscript.sh

(Обратите внимание на точку и пробел перед именем скрипта.)

Tzachi.e
источник
2
Это круто, и, наверное, это полезно знать. Что делает последний, хотя (как это работает)? Это, вероятно, лучшее решение в этой теме.
Иона
2
fwiw, в разделе комментариев Адама Лисса «ответ, ephemient отвечает на вопрос, что такое«. » является. Это то же самое, чтоsource
g19fanatic
1
Будьте осторожны, «источник» - это башизм. т.е. dash (Debian по умолчанию для / bin / sh) не поддерживает его, а "." работает как положено.
алло
Отличный ответ! также, если он myscript.shнаходится в каталоге, включенном в $ PATH, вы можете получить его из любого места без указания полного пути.
CodeBrew
Это сработало для меня лучше всего. Это решение является самым простым (+1).
HuserB1989
96

Чтобы создать скрипт bash, который перейдет в выбранный каталог:

Создать файл скрипта

#! / Bin / ш
# file: / scripts / cdjava
#
cd / home / askgelal / projects / java

Затем создайте псевдоним в вашем файле запуска.

#! / Bin / ш
# file /scripts/mastercode.sh
#
псевдоним cdjava = '. / Скрипты / 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), а не запускается как сценарий оболочки».
Майкл
Еще один совет (для bash): если вы используете переменные в вашем скрипте, то, поскольку сценарий sourced (через alias), эти переменные попадут в вашу оболочку. Чтобы избежать этого, сделайте всю работу скрипта в функции и просто вызовите его в конце скрипта. Внутри функции объявите любые переменные local.
Rhubbarb
в 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 - это / Users / 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
Джонатан Леффлер
источник
31

Используйте exec bashв конце

Скрипт bash работает в текущей среде или в дочерних, но никогда в родительской среде.

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

Если это так, просто запустите дочерний экземпляр bash в конце скрипта:

#!/usr/bin/env bash
cd /home/tree/projects/java
exec bash
Серж Строобандт
источник
16
Это создает новую подоболочку. При вводе exitвы вернетесь в оболочку, в которой вы запустили этот скрипт.
tripleee
1
Когда сценарий вызывается несколько раз, создается впечатление, что он создает вложенные оболочки, и мне нужно много раз вводить команду exit. Можно ли это как-то решить?
Влад Савицкий
Посмотрите другие ответы: используйте псевдоним, используйте «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$ 
kaelhop
источник
2
Какая разница между . somethingи ./something?? Этот ответ сработал для меня, и я не понимаю, почему.
Дракорат
. somethingпозволяет запускать скрипт из любого места, ./somethingтребует , чтобы вы находились в каталоге, в котором хранится файл.
Projjol
Можете ли вы поставить точку на линии Шебанга? #!. /bin/bash?
Зигфрид
20

просто запустите:

cd /home/xxx/yyy && command_you_want
warhansen
источник
Это сработало для меня, спасибо @warhansen
Ашиш Кумар
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 / projects / java

Надеюсь, это поможет!

workdreamer
источник
1
@WM В конечном итоге вы можете сделать «$ switchp» без каких-либо параметров, чтобы перейти непосредственно к дереву проекта
workdreamer
2
@workdreamer Функциональный подход, который вы описали выше, решил небольшую проблему при переходе к подпапкам, которые имеют ту же родительскую папку в моей системе. Спасибо.
WM
10

Вы можете сделать следующее:

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

РЕДАКТИРОВАТЬ: Это также может быть «пунктирной», чтобы предотвратить создание последующих оболочек.

Пример:

. ./previous_script  (with or without the first line)
Thevs
источник
1
Это становится довольно запутанным после нескольких запусков. Вам придется exit(или ctrl + d) несколько раз, чтобы выйти из оболочки, например. Псевдоним намного чище (даже если команда оболочки выводит каталог, и это cd к выводу - псевдоним что-то = "CD getnewdirectory.sh")
дбн
Обратите внимание на «exec». Это делает замену старой оболочки.
Thevs
2
Exec заменяет только вложенную оболочку, в которой выполнялась команда cd, а не оболочку, в которой выполнялся сценарий. Если бы вы поставили пунктирный сценарий, вы были бы правы.
Джонатан Леффлер
Сделать это «пунктирной» - нет проблем. Я только что предложил решение. Это не зависит от того, как вы запускаете это.
Thevs
2
Хе-хе, я думаю, что лучше просто «расставить точки» однострочный сценарий только с помощью команды cd :) В любом случае, я сохраню свой ответ ... Это будет правильный глупый ответ на неправильный глупый вопрос :)
Thevs
7

Это только изменяет каталог для самого скрипта, в то время как ваш текущий каталог остается прежним.

Вы можете использовать символическую ссылку вместо этого. Это позволяет вам сделать «ярлык» для файла или каталога, так что вам нужно всего лишь напечатать что-то вроде cd my-project.

Джереми Рутен
источник
Наличие такой символической ссылки в каждом каталоге было бы неприятностью. Можно было бы поместить символическую ссылку в $ HOME, а затем выполнить 'cd ~ / my-project'. Честно говоря, проще использовать CDPATH.
Джонатан Леффлер
7

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

alias project=". project"

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

robertmoggach
источник
Это именно то, что мне нужно, чтобы поддерживать весь мой скрипт и просто вызывать его из bash и поддерживать текущий сеанс - это ИДЕАЛЬНЫЙ ответ.
Мэтт Ниндзя
7

Вы можете объединить псевдоним и скрипт,

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

при условии, что скрипт повторяет путь назначения. Обратите внимание, что это обратные черты вокруг имени скрипта. 

Например, ваш скрипт может быть

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

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

JA Faucett
источник
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
Майклу
5

В вашем файле ~ / .bash_profile. добавить следующую функцию

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

Перезапустите терминал, и вы можете ввести

move_me 

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

mihai.ciorobea
источник
3

Вы можете использовать оператор &&:

cd myDirectory && ls

Джек Бауэр
источник
Даунвот: Это не попытка ответить на реальный вопрос здесь. Вы можете запустить две команды в приглашении (с помощью, &&если вы хотите, чтобы вторая была условной по отношению к первой, или просто с ;или новой строкой между ними), но вставив это в сценарий, вы вернетесь назад к «почему родительская оболочка не использует новый каталог, когда на 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, чтобы обеспечить вас изучением закладок каталога оболочки.

thomasd
источник
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
Это решение великолепно в своей простоте и гибкости.
Кьяртан
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)

jithu83
источник
3
Появление нового bash будет означать, что вы потеряете свою историю, и она начнется заново.
Тиш
2

Через некоторое время, но я сделал следующее:

создать файл с именем case

вставьте в файл следующее:

#!/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

соответственно

В моем случае путь намного длиннее - эти парни суммировали его с информацией ранее.

Крис
источник
1
cdВ псевдониме является излишним и безвкусным; псевдоним должен просто ~ / case` вместо. Также caseявляется зарезервированным ключевым словом, поэтому довольно плохой выбор для имени.
tripleee
1

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

shopt -s cdable_vars

в вашем ~/.bashrcпозволяет cdсодержимое переменных среды.

Создайте такую ​​переменную среды:

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

и вы можете использовать:

cd myjava

Другие альтернативы .

Готье
источник
0

Если вы используете рыбу в качестве оболочки, лучшее решение - создать функцию. В качестве примера, учитывая исходный вопрос, вы можете скопировать 4 строки ниже и вставить их в командную строку вашей рыбы:

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,
просто поместите скрипт в локальный каталог bin (/ usr / local / bin)
и вставьте

alias p='. p'

в вашем .bashrc

Годзилла
источник
что это делает? похоже, что он рекурсивно запускается сам, хотя я уверен, что если вы запустите его раньше, чем он не будет;)
Райан Тейлор
0

Обратите внимание на обсуждение Как мне установить рабочий каталог родительского процесса?

Он содержит некоторые хакерские ответы, например https://stackoverflow.com/a/2375174/755804 (изменение каталога родительского процесса с помощью gdb, не делайте этого) и https://stackoverflow.com/a/51985735/755804 ( команда, tailcdкоторая внедряет dd-имя cd во входной поток родительского процесса; в идеале она должна быть частью bash, а не хаком)

18446744073709551615
источник
0

Это старый вопрос, но я очень удивлен, что не вижу здесь этого трюка

Вместо использования CD вы можете использовать

export PWD=the/path/you/want

Не нужно создавать подоболочки или использовать псевдонимы.

Обратите внимание, что вы должны убедиться, что / path / you / want существует.

Юрий Нудельман
источник
К сожалению, не сработало.
ttfreeman
0

Как объяснено в других ответах, вы изменили каталог, но только внутри вложенной оболочки, которая запускает сценарий . это не влияет на родительскую оболочку.

Одним из решений является использование функций bash вместо сценария bash ( 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
}
intika
источник
-2

Вы можете выполнить несколько строк в одной и той же подоболочке, если заканчиваете строки обратной косой чертой.

cd somedir; \
pwd
Максимум
источник