Используйте перенаправление вывода вместо того, чтобы указать имя файла. Это относится ко многим командам, не просто tar.
Конечно, Вы имеете set -o noclobber
(также записанный set -C
) в Вашем .bashrc
, право?
$ tar zc >foo.tex foo.bib
bash: foo.tex: cannot overwrite existing file
Если Вы часто даете эту команду, не вводите ее вручную: запишите make-файл.
foo.tar.gz: foo.tex foo.bib
tar czf $@ $^
Используйте tarballs только, когда необходимо будет отправить их кому-то еще, для не создания резервных копий. Вместо того, чтобы делать резервные копии старых версий, используйте управление версиями (CVS, Подвижный, Мерзавец, и т.д., безотносительно плаваний Ваша лодка). (Действительно создайте резервную копию репозитория в случае, если Ваши дисковые сбои или Вы случайно искажаете или стираете репозиторий.)
Ваша проблема в том, что скрипт в вашем ... /bin
директории выполняется
в другом окружении оболочки - его окружение не переживает его выполнения, и поэтому определение x() { ... ; }
не сохраняется в текущем окружении оболочки, когда он завершается.
Когда вы . ./somescript.sh
(или source
в некоторых оболочках, таких как bash
и zsh
), текущая оболочка считывает сценарий в текущее окружение и выполняет его содержимое, как если бы он был выдан из приглашения - поэтому x() { ... ; }
определяется в текущем окружении оболочки - вашей интерактивной оболочке.
В принципе, проблему можно продемонстрировать следующим образом:
sh <<\HEREDOC_CMD_FILE #runs sh shell with heredoc input file
{ #begins current shell compound expression
( #begins subshell compound expression
x() { echo 'I am function x.' ; } #define function x
x #x invoked in subshell
) #ends subshell compound expression
x #x invoked in current shell
} #ends current shell compound expression
#v_end_v
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
sh: line 6: x: command not found
Точно так же, как среда, определенная в ( : subshell)
в приведенном выше примере, не переживет его завершения, не переживет ее и среда, определенная в выполняемом вами сценарии. Аналогично, когда sh
читает HEREDOC_CMD_FILE
, он выполняет ту же функцию, что и ваш source ../file
, и запускает его содержимое в текущей среде выполнения оболочки - хотя его среда является дочерней по отношению к оболочке, выдающей приглашение, в которой он выполняется, и поэтому ни одно из его окружений не переживает его завершения оттуда. Однако вы можете заставить его сделать это, например:
. /dev/fd/0 <<\HEREDOC_CMD_FILE && x
x() { echo 'I am function x.' ; }
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
... что практически в точности соответствует тому, что вы делаете с source ${script}
, за исключением того, что здесь мы .dot
используем содержимое stdin
, тогда как вы используете содержимое файла на диске
Основное различие, однако, между ( : subshell)
и выполняемым скриптом заключается в том, как выглядит среда выполнения выполняемого скрипта. Когда вы выполняете сценарий, ему предоставляется новая среда для работы - поэтому любые переменные, объявленные в вашей текущей оболочке, не переносятся в ее среду, если они явно не экспортированы или не объявлены в ее командной строке. Это поведение можно продемонстрировать следующим образом:
{ x() { printf "$FMT" "$0" "$var2" x ; } #describe env
export FMT='argv0: %s\t\tvar2: %s\t\tI am function %s.\n' \
var1=val1 #var1 and FMT are explicitly exported
var2=shell_val2 #var2 is not
cat >./script && #read out stdin to >./script
chmod +x ./script && #then make ./script executable
var3=val3 ./script #then define var3 for ./script's env and execute
} <<\SCRIPT ; x ; y #send heredoc to block's stdin then call {x,y}()
#!/usr/bin/sh
#read out by cat to >./script then executed in a separate environment
y() { printf "$FMT" "$0" "$var2" y ; } #describe env
echo "${var1:-#var1 is unset or null}" #$var1 if not unset or null else :-this}
echo "${var2:-#var2 is unset or null}"
echo "${var3:-#var3 is unset or null}"
export var2=script_val2
x ; y #run x() ; y() in script
#v_end_v
SCRIPT
###OUTPUT###
val1
#var2 is unset or null
val3
./script: line 8: x: command not found
argv0: ./script var2: script_val2 I am function y.
argv0: sh var2: shell_val2 I am function x.
sh: line 18: y: command not found
Это поведение отличается от поведения ( : subshells)
, запускаемых в подсказке или иным образом как дочерние элементы текущей оболочки, поскольку они автоматически наследуют окружение своего родителя, тогда как - как показано выше - исполняемые дочерние элементы требуют явного экспорта
ed.
var1=val1 ; export var2=val2
x() { echo 'I am function x.' ; }
(
printf '%s\n' "$var1" "$var2"
x
)
###OUTPUT###
val1
val2
I am function x.
Последнее, что я должен отметить, это то, что не существует переносимого способа экспорта функций из родительской оболочки в исполняемый сценарий без .dot
цитирования файла, содержащего определение функции в исполняемом сценарии, как вы это делаете с source
в вашей текущей оболочке. В принципе, вы не можете переносить экспорт функции
, как это можно сделать с переменными. Хотя, я полагаю, вы можете export fn_def='fn() { : fn body ; }'
затем eval "$fn_def"
в вашем выполняемом скрипте. И, конечно, любое дочернее окружение - исполненное или иное - умирает вместе с дочерним.
Так что если вам нужна функция, определенная в скрипте, но вы не хотите использовать исходный текст самого скрипта, вам придется прочитать функцию как вывод от некоторой команды и eval
ее.
eval "$(cat ./script)"
Но это практически то же самое, что и . ./script
- просто менее эффективно. С тем же успехом можно просто использовать исходный текст.
Лучший способ сделать это - удалить определение функции из самого скрипта и поместить его в собственный файл, а затем вызывать его как из скрипта, так и из текущей оболочки, когда он вам нужен. Например, так:
{
echo 'x() { echo "I am function x and my argv0 is ${0}." ; }' >./fn_x
echo '. ./fn_x ; x' >./script
chmod +x ./script && ./script
. ./fn_x ; x
}
###OUTPUT###
I am function x and my argv0 is ./script.
I am function x and my argv0 is sh.
Чтобы эта функция всегда была доступна в интерактивной оболочке, добавьте . /path/to/fn_x
в файл ENV
вашей оболочки. Например, для bash
со сценарием, содержащим функции под названием foo
, расположенным в /usr/bin
, вы можете добавить эту строку в ~/.bashrc
:
. /usr/bin/foo
Если сценарий по какой-либо причине недоступен в этом месте во время запуска, ваша оболочка все равно будет считывать и использовать остальную часть файла ENV
, как и ожидалось, хотя в stderr
будет выведено диагностическое сообщение, чтобы сообщить вам, что возникла проблема с источником файла определения функции.
Ответ mikeserv хорош для подробностей того, что происходит «за кулисами», но я считаю, что здесь оправдан другой ответ, поскольку он не содержит простого полезного ответа на точный вопрос заголовка:
Как мне загрузить функции этого файла сценария, не создавая его каждый раз?
Ответ: Загрузите его с вашего .bashrc
или вашего .bash_profile
, поэтому он доступен в каждой запущенной вами оболочке.
Например, у меня в .bash_profile
есть следующее:
if [ -d ~/.bash_functions ]; then
for file in ~/.bash_functions/*.sh; do
. "$file"
done
fi