Это вопрос, который я собирался разместить здесь несколько недель назад. Как и terdon , я понял, что .bashrc
используется только для интерактивных оболочек Bash, поэтому нет необходимости в .bashrc
, чтобы проверить, работает ли он в интерактивная оболочка. Как ни странно, все дистрибутивы, которые я использую (Ubuntu, RHEL и Cygwin), имели какой-либо тип проверки (тестирование $ -
или $ PS1
) для обеспечения текущей оболочки. интерактивен. Мне не нравится программирование культа карго , поэтому я приступил к пониманию цели этого кода в моем .bashrc
.
После исследования проблемы я обнаружил, что удаленные оболочки обрабатываются по-другому. Хотя неинтерактивные оболочки Bash обычно не запускают команды ~ / .bashrc
при запуске, особый случай возникает, когда оболочка вызывается удаленным демоном оболочки :
Bash пытается определить, когда он запускается со своим стандартным вводом , подключенным к сетевому соединению, как при выполнении удаленным демоном оболочки , обычно
rshd
, или демон защищенной оболочкиsshd
.Если Bash определяет, что он выполняется таким образом, он читает и выполняет команды из ~ / .bashrc, если этот файл существует и доступен для чтения. Этого не произойдет, если вызывается какsh
. Параметр- norc
может использоваться для запрета этого поведения, и параметр- rcfile
могут использоваться для принудительного чтения другого файла, но {{ 1}} ниrshd
, ниsshd
обычно не вызывают оболочку с этими параметрами и не позволяют их указывать.
Вставьте следующее в начало удаленного .bashrc
. (Если .bashrc
получен из .profile
или .bash_profile
, временно отключите это во время тестирования):
echo bashrc
fun()
{
echo functions work
}
Выполните следующие команды локально:
$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
i
в $ -
указывает, что оболочка неинтерактивная . -
в $ 0
означает, что оболочка не является оболочкой входа в систему . Функции оболочки, определенные в удаленном .bashrc
, также могут быть запущены:
$ ssh remote_host fun
bashrc
functions work
Я заметил, что ~ / .bashrc
используется только , когда команда указывается в качестве аргумента для ssh
. Это имеет смысл: когда ssh
используется для запуска обычной оболочки входа, запускаются .profile
или .bash_profile
(и .bashrc
предоставляется только в том случае, если это явно сделано одним из этих файлов).
Основное преимущество, которое я вижу в наличии .bashrc
, полученный при запуске (неинтерактивной) удаленной команды, позволяет запускать функции оболочки. Однако большинство команд в типичном .bashrc
актуальны только в интерактивной оболочке, например, псевдонимы не раскрываются, если оболочка не является интерактивной.
Обычно это не проблема, когда rsh
или ssh
используются для запуска интерактивной оболочки входа или когда используются неинтерактивные оболочки для выполнения команд. Однако это может быть проблемой для таких программ, как rcp
, scp
и sftp
, которые используют удаленные оболочки для передачи данных.
Оказывается, оболочка удаленного пользователя по умолчанию (например, Bash) неявно запускается при использовании команды scp
. На странице руководства об этом не упоминается - только упоминается, что scp
использует ssh
для передачи данных. Это приводит к тому, что , если .bashrc
содержит какие-либо команды, которые выводят на стандартный вывод, передача файлов завершится ошибкой , например,
scp завершится неудачно без ошибок .
См. Также этот связанный отчет об ошибке Red Hat 15 лет назад, scp прерывается, когда есть команда echo в / etc / bashrc (который в конечном итоге был закрыт как WONTFIX
).
scp
и sftp
не работают SCP (безопасная копия) и SFTP (протокол безопасной передачи файлов) имеют свои собственные протоколы для локальный и удаленный концы обмениваются информацией о передаваемых файлах. Любой неожиданный текст с удаленного конца (ошибочно) интерпретируется как часть протокола, и передача не выполняется. Согласно FAQ из Snail Book
Что часто случается, однако,заключается в том, что есть операторы либо в системных файлах , либо в файлах запуска пользовательской оболочки на сервере (
.bashrc
,.profile
,] /etc/csh.cshrc
,.login
и т. д.), которые выводят текстовые сообщения при входе в систему, предназначенные для чтения людьми (например,fortune
,эхо «Привет!»
и т. Д.).Такой код должен производить вывод только при интерактивном входе в систему, когда к стандартному вводу прикреплен
tty
. Если он не выполнит этот тест, он вставит эти текстовые сообщения туда, где они не принадлежат: в этом случае, загрязняя поток протокола междуscp2
/] sftp
иsftp-сервер
.Причина, по которой файлы запуска оболочки вообще актуальны, заключается в том, что
sshd
использует оболочку пользователя при запуске любых программ от имени пользователя (используя например, / bin / sh -c "команда"). Это традиция Unix и имеет преимущества:
- Обычные настройки пользователя (псевдонимы команд, переменные среды, umask, и т. Д.) Действуют при запуске удаленных команд.
- Обычная практика настройки оболочки учетной записи на / bin / false, чтобы отключить , это не позволит владельцу запускать какие-либо команды, если аутентификация по какой-то причине все же будет случайно успешной.
Для тех, кто интересуется деталями работы SCP, я нашел интересную информацию в Как работает протокол SCP , которая включает детали о Запуск scp с профилями говорящей оболочки на удаленная сторона? :
Например, это может произойти, если вы добавите это в свой профиль оболочки в удаленной системе :
echo ""
Почему он просто зависает? Это происходит из-за того, как
scp
в режиме source ожидает подтверждения первого сообщения протокола. Если это не двоичный 0, он ожидает, что это уведомление об удаленной проблеме, и ждет еще символов, чтобы сформировать сообщение об ошибке, пока не появится новая строка. Поскольку вы не печатали еще одну новую строку после первой, ваш локальныйscp
просто остается в цикле и блокируется наread (2)
. Тем временем, после того, как профиль оболочки был обработан на удаленной стороне,scp
в режиме приемника был запущен, который также блокируетread (2)
, ожидая двоичного нуля, обозначающего начало передачи данных.
Большинство операторов в типичном .bashrc
полезны только для интерактивной оболочки - не при выполнении удаленных команд с rsh
или ssh
. В большинстве таких ситуаций установка переменных оболочки, псевдонимов и определяющих функций нежелательна, а вывод любого текста в стандартный формат является опасным при передаче файлов с помощью таких программ, как scp
или sftp
.Выход после проверки того, что текущая оболочка не интерактивна, является наиболее безопасным поведением для .bashrc
.
Потратьте пару шагов на то, чтобы пропустить лишнее. Это ускорит процесс в целом.
declare -a lst=( Cr Hf Mo Nb Ta Ti V W Zr ) # make an array
for a in ${lst[@]} # for each element
do for b in ${lst[@]:1} # for each but the 1st
do [[ "$b" > "$a" ]] || continue # keep them alphabetical and skip wasted work
for c in ${lst[@]:2} # for each but the first 2
do [[ "$c" > "$b" ]] || continue # keep them alphabetical and skip wasted work
for d in ${lst[@]:3} # for each but the first 3
do [[ "$d" > "$c" ]] || continue # keep them alphabetical and skip wasted work
mkdir "$a$b$c$d" && echo "Made: $a$b$c$d" || echo "Fail: $a$b$c$d"
done
done
done
done
Пропуски избыточности предназначены для того, когда начинаются более поздние циклы, например, когда внешний цикл находится на элементе 4, а второй цикл все еще находится на 3 или 4. Они пропускают их, потому что они не будут алфавитными комбинациями. Это также гарантирует отсутствие повторов. Это сгенерировало 126 различных каталогов без ошибок в 0m8.126s в git bash на моем ноутбуке без дополнительных оболочек, кроме mkdir
.
Использование Perl и модуля Algorithm::Combinatorics
:
perl -MAlgorithm::Combinatorics=combinations -e '$"=""; map { mkdir "@{$_}N" } combinations([qw(Cr Hf Mo Nb Ta Ti V W Zr)], 4)'
Это создаст 126 каталогов, которые вы получите из всех комбинаций четырех включенных слов. Имя каждого каталога будет иметь в конце N
. Отдельные слова всегда будут встречаться в алфавитном порядке в именах каталогов из-за исходного порядка массива в коде.
Как правильный Perl-скрипт:
#!/usr/bin/perl
use strict;
use warnings;
use English;
use Algorithm::Combinatorics qw(combinations);
# When interpolating a list in a string (@{$ARG} below), don't use a delimiter
local $LIST_SEPARATOR = "";
# Get all combinations, and create a directory for each combination
map { mkdir "@{$ARG}N" } combinations( [qw(Cr Hf Mo Nb Ta Ti V W Zr)], 4 );
Это будет выполняться почти мгновенно и легко расширяться, чтобы включать дополнительные слова или длины комбинаций.
Вероятно, вы могли бы сделать что-то похожее на Python...
Рекурсивная реализация оболочки (Просто для развлечения, рекурсивные функции оболочки редко бывают очень эффективными):
#!/bin/sh
build_combinations () {
set_size=$1
shift
if [ "$set_size" -eq 0 ]; then
printf 'N'
else
for token do
shift
for reminder in $(build_combinations "$(( set_size - 1 ))" "$@")
do
printf '%s%s\n' "$token" "$reminder"
done
done
fi
}
build_combinations 4 Cr Hf Mo Nb Ta Ti V W Zr | xargs mkdir
Идея, полученная после прочтения ответа studog и вдохновения из различных фрагментов ответа на вопрос StackOverflow .
Обратите внимание, что преимуществом этого решения является то, что имена каталогов всегда заканчиваются на N
. Ветвь рекурсивной остановки выводит N
, а не пустую строку, благодаря чему все работает. Без его (печати пустой строки или новой строки )циклу с подстановкой команд нечего было бы зацикливать и не было бы вывода (из-за значения по умолчанию переменной IFS
)].
Улучшение ответа @n.st, в котором используется тот факт, что элементы изначально отсортированы. Это также немного яснее, на мой взгляд.
#!/bin/bash
elements=(Cr Hf Mo Nb Ta Ti V W Zr)
len=${#elements[@]}
(( a_end = len - 3 ))
(( b_end = len - 2 ))
(( c_end = len - 1 ))
(( d_end = len - 0 ))
(( a = 0 ))
while (( a < a_end )); do
(( b = a + 1 ))
while (( b < b_end )); do
(( c = b + 1 ))
while (( c < c_end )); do
(( d = c + 1 ))
while (( d < d_end )); do
mkdir "${elements[$a]}${elements[$b]}${elements[$c]}${elements[$d]}"
(( d++ ))
done
(( c++ ))
done
(( b++ ))
done
(( a++ ))
done
Ключевая часть каждого внутреннего цикла начинается со следующего индекса элемента из окружающего цикла. Это довольно распространенный шаблон для создания всех комбинаций списка элементов.
Время работы:
user@host:~/so$ time./do.sh
real 0m0.140s
user 0m0.085s
sys 0m0.044s
с
user@host:~/so$ ls -1d Cr* Hf* Mo* Nb* Ta* Ti* V* W* Zr* | wc -l
ls: cannot access 'V*': No such file or directory
ls: cannot access 'W*': No such file or directory
ls: cannot access 'Zr*': No such file or directory
126
#/bin/sh
# shellcheck disable=SC2046
# ^ word-splitting by the shell is intentional in this file
elems="Cr Hf Mo Nb Ta Ti V W Zr"
for a in $elems
do
for b in $elems
do
for c in $elems
do
for d in $elems
do
# for a set of any four elements:
# string them together, separated by NUL-bytes
# sort them lexicographically...
# ... with NUL separating the elements (-z)
# ... and eliminate duplicates (-u)
# then replace the NUL bytes with line breaks
# allow the shell to split on those line breaks
# and chuck the resulting chunks into $1, $2, etc
set -- $(printf '%s\0' "$a" "$b" "$c" "$d" | sort -z -u | tr "\0" "\n")
# only if the current selection of elements consisted of four
# different ones (remember we eliminated duplicates):
if [ $# -eq 4 ]
then
# create a directory, don't error out if it already exists (-p)
mkdir -p "$(printf '%s' "$@")"
fi
done
done
done
done
Не очень эффективные(sort
вызовы даже для очевидных не -кандидатов и множественные mkdir
вызовы для одного и того же имени каталога ), но максимум 9 4 = 6561 итераций внутренний цикл и с тем, что это единственный -сценарий использования, я не думаю, что стоит тратить много времени на оптимизацию.
Редактировать:
Тест на Xeon E3 -1231v3 безmkdir
:
./elemdirs.sh > /dev/null 11.66s user 1.73s system 173% cpu 7.725 total
и с ним:
./elemdirs.sh > /dev/null 13.80s user 2.16s system 156% cpu 10.215 total
Выдает 126 каталогов, ожидаемое количество комбинаций с k = 4, n = 9.