#bash #ssh #escaping #rm
Вопрос:
В BASH следующая команда удаляет все в каталоге, кроме одного файла:
rm -rf !(filename.txt)
Однако в SSH та же команда ничего не меняет в каталоге и возвращает следующую ошибку: -jailshell: !: событие не найдено
Итак, я сбежал !
с
помощью (скобки также требуют побега), но это все еще не работает:
rm -rf !(filename.txt)
Он не возвращает никаких ошибок, и в каталоге ничего не изменилось.
Возможно ли вообще выполнить эту команду в SSH? Я нашел обходной путь, но если это возможно, это значительно ускорит процесс.
Я подключаюсь к серверу ssh, используя псевдоним ниже:
alias devssh="ssh -p 2222 -i ~/.ssh/private_key user@host"
Комментарии:
1. Пожалуйста, добавьте свою команду ssh к вашему вопросу (без комментариев).
2. @Сайрус, это там. Это второй кодовый блок.
3. Я думаю, что Сайрус имел в виду полную
ssh
команду (т. Е.ssh user@host ...
), а не только команду, которая выполняется внутри ssh.4. Откуда это
-jailshell:
берется?5. @Cyrus, он возвращается в окне терминала.
Ответ №1:
!(filename.txt)
это extglob, будущее bash, которое, возможно, придется включить. Убедитесь, что ваш ssh-сервер работает под управлением bash и что extglob включен:
ssh user@host "bash -O extglob -c 'rm -rf !(filename.txt)'"
Или используя свой псевдоним:
devssh "bash -O extglob -c 'rm -rf !(filename.txt)'"
Если вы уверены, что удаленная система использует bash по умолчанию, вы также можете удалить эту bash -c
деталь. Но ваше сообщение об ошибке указывает на то, что сервер ssh запущен jailshell
.
ssh user@host 'shopt -s extglob; rm -rf !(filename.txt)'
devssh 'shopt -s extglob; rm -rf !(filename.txt)'
Комментарии:
1. Я попытался установить extglob с помощью
bash -O extglob -c 'rm -rf !(filename.txt)
, и он уничтожил все содержимое во всем каталоге public_html.2. Кажется, все было бы правильно, если бы
filename.txt
в этом каталоге ничего не было. Вы уверены, что такой файл был там раньше?3. @Thomas: Я предлагаю заменить
rm
наecho rm
первый.4. Вот локальный тест , показывающий, что команда работает должным образом.
5. Я просто буду очень рад, что это сервер разработчиков.
Ответ №2:
Я бы так не поступил. Я бы не стал полагаться на то, что баш находится на пульте дистанционного управления, и я бы не стал полагаться ни на какие башизмы. Я бы использовал:
$ ssh user@host 'rm $(ls | grep -v "^filename.txt$")'
Если бы я хотел защититься от возможности того, что каталог может быть пустым, я бы назначил вывод $(...)
переменной и проверил его на пустоту. Если бы я был обеспокоен тем, что команда может оказаться слишком длинной, я бы записал имена в файл и отправил вывод grep в rm с помощью xargs.
Если бы это было слишком сложно, я бы скопировал сценарий на пульт дистанционного управления и выполнил его.
Комментарии:
1. Я был бы обеспокоен не только длительным командованием. Если бы у вас были файлы
important.txt
not important.txt
и вы побежалиrm $(ls | grep -v "^important.txt$")
, то вас ждал бы сюрприз .2. ls , записанный в канал, создает одну строку на имя.
^important.txt$
совпадает со всей линией, закрепленной спереди рядом^
. Он не совпадаетnot important.txt
, чтоgrep -v
приведет к печати, потому что смысл перевернут.3. Да, все это правильно. Но вы пробовали это сами (см. Ссылку в моем предыдущем комментарии). Проблема в том, что без кавычек
$()
происходит разделение слов .not important.txt
затем превращается вnot
и.important.txt
Кроме того, глобусы в именах файлов будут расширяться.4. @Socowi, спасибо, теперь я понимаю вашу точку зрения. Я бы все равно последовал своему совету, чтобы использовать стандартный синтаксис, и вашему, чтобы тщательно протестировать, особенно с **rm**(1).