Чи є цитування файлів достатньою безпекою для запуску `xargs sudo rm -rf`?


10

Я написав сценарій, який видаляє всі, крім двох останніх файлів у папці:

#!/bin/bash
ls -1 --quoting-style=shell-always /path/to/some/folder \
    | head -n -2 \
    | xargs printf -- "'/path/to/some/folder/%s'\n" \
    | xargs sudo rm -rf

Цей сценарій буде виконуватись як робота з хроном щодня.

Міркування такі:

  1. Отримати список усіх файлів, що використовуються ls -1(так що я отримую один файл у рядку);

  2. Видаліть останні два зі списку за допомогою head -n -2;

  3. Оскільки lsдрукує відносні шляхи, використовуйте xargs printfріч, щоб додати шлях до папки та зробити її абсолютним шляхом;

  4. Надіслати їх у sudo rm -rfкористування xargs.

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

Проблема в тому, що: sudo rm -rf страшно. xargs sudo rm -rfнеймовірно страшно.

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

file with / spaces.txt

що може призвести до надто страшного sudo rm -rf /.

EDIT: Моя помилка, назви файлів не можуть містити /, тому ця конкретна проблема не трапиться, але все ще стоїть питання про те, чи є інші ризики чи ні.

Ось чому я використовую --quoting-style=shell-always, це повинно запобігти будь-яким хитростям з файлами з пробілами. Але зараз мені цікаво, чи може хтось бути надзвичайно розумним із пробілами та цитатами у назві файлу, можливо.

Чи безпечний мій сценарій?


Примітка. Мені це потрібно, sudoоскільки я віддаляю папку віддалено (з картографічного мережевого накопичувача за допомогою mount), і я не міг змусити її працювати без судо.


3
Чи думали ви робити щось на кшталт printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
steeldriver

Чи може /бути створений файл із символом в імені, я намагаюся досягти цього ще тут
Джордж Удосен

3
@George Ні, ім'я файлу не може містити косу рису.
wjandrea

Тож, коли ОП каже розумній людині, мені було цікаво ...
Джордж Удосен

6
Просто тому, що ви аналізуєте lsвихід, це вже погано написана команда, навіть із цитуванням. lsЯ також вважаю, що також використовується locale для порядку сортування, я вважаю, тому я не бачу, у чому мета headвидалення останніх 2 (якщо ви не намагаєтеся позбутися .і ..яких iirc не дозволено як аргументи rm. Просто використовуйте find /path/to/folder -type f delete. І ні, sudoякщо ви біжите від cron - cron вже на рівні коренів
Сергій Колодяжний

Відповіді:


10

У Linux будь-який символ є дійсним іменем файлу, що становить символ, за винятком:

  • \0 (ASCII NUL): використовується для завершення рядка в C
  • / (пряма косою рисою): як використовується для розділення шляху

Отже, ваш підхід, безумовно, не спрацює у багатьох випадках, як ви можете собі уявити, наприклад, чи обробляє він новий рядок ( \n) у назві файлу? ( Підказка: Ні ).

Кілька приміток:

  • Не розбирайте ls; використовувати спеціалізовані інструменти (є принаймні один для більшості випадків використання)
  • Спілкуючись з іменами, спробуйте використати відокремлений результат NUL, який забезпечується майже всіма інструментами GNU, які працюють з такими даними
  • Будьте уважні, коли трубопроводи, переконайтесь, що обидві програми можуть зрозуміти розділення NUL
  • Щоразу, коли ви звертаєтесь xargs, дивіться, чи можете ви втекти find ... -exec; у більшості випадків вам буде добре лише findодин

Думаю, що зараз це ви змусите. steeldriver вже надав ідею розділеної NUL у коментарі ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), використовуйте це як вихідну точку.


Дякую за вашу відповідь :) Я думаю, що вам слід навести коментар steeldriver, а не просто згадати (оскільки коментарі не є постійними). Я findтакож розберуся, дякую за пропозицію.
Педро А

Однак у мене є одне питання: я не розумію, що ви маєте на увазі під вашим підходом, безперечно, спрацює в багатьох випадках, як ви можете собі уявити »-« не працювати », як у« не безпечно »чи« не небезпечно »? Тому що "ваш підхід" стосується мене, а не зловмисного користувача, а ваші попередні заяви йдуть на мою користь, тому я плутаюся.
Педро А

@PedroA Ви вас :) Як я сказав, так як всі символи дійсні крім зазначених двох, ви повинні бути в змозі уявити собі численні випадки ваш lsсинтаксичний підхід зазнає невдачі , наприклад , навіть якщо взяти до уваги новий рядок в імені файлу?
heemayl

О, новий рядок у назві файлу ... Я про це не думав. Якщо ви не проти додати це до своєї відповіді :) Також, вибачте, запитайте, але що head -zробити? Це звучить смішно, але у мене немає manні infoв моєму Linux-контейнері CoreOS ... Не можу знайти і в Інтернеті. Я отримуюhead: invalid option 'z'
Педро A

@PedroA Newline - це лише один випадок, є багато таких, про які ви могли здогадатися . Вам потрібна GNU head(постачається разом з GNU coreutils). Ось онлайн-версія: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl

3

xargs підтримує деяке цитування: з одинарними цитатами, подвійними цитатами або зворотною косою рисою, що дозволяє йому приймати довільні аргументи¹, але з синтаксисом, відмінним від синтаксису цитата оболонок Борна.

Реалізація GNU, lsяк знайдено в Ubuntu, не має жодного режиму котирування, сумісного з xargsформатом введення.

Він ls --quoting-style=shell-alwaysсумісний з оболонками ksh93, bash та zsh, котируючи синтаксис, але лише тоді, коли результат lsінтерпретації оболонки інтерпретується в тій самій локалі, що lsі при виведенні. Також слід уникати деяких місцеположень, таких як BIG5, BIG5-HKSCS, GBK або GB18030.

Тож із цими оболонками ви дійсно можете:

typeset -a files
eval "files=($(ls --quoting-style=shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...

Але це має невелику перевагу перед:

files=(*(N))                 # zsh
files=(~(N)*)                # ksh93
shopt -s nullglob; files=(*) # bash

Єдиний випадок, коли стає корисним, коли ви хочете скористатися -tопцією lsсортування файлів за mtime / atime / ctime або -S/ -V. Але навіть тоді ви можете також використовувати zsh:

files=(*(Nom))

наприклад, сортувати файли за mtime (використовувати oLдля -Sта nдля -V).

Щоб видалити всі, крім двох останніх змінених звичайних файлів:

rm -f -- *(D.om[3,-1])

Still все ще є деякі обмеження довжини (як execve()і в деяких не-GNU- xargsреалізаціях, значно нижчі довільні), а деякі не-GNU- xargsреалізації будуть задушені введеннями, що містять послідовності байтів, що не утворюють дійсних символів.

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