¿Citar nombres de archivos es suficiente seguridad para ejecutar `xargs sudo rm -rf`?

10

Escribí un script que elimina todos excepto los dos últimos archivos en una carpeta:

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

Este script se ejecutará como un trabajo cron todos los días.

El razonamiento es como sigue:

  1. Obtenga una lista de todos los archivos usando ls -1(para que obtenga un archivo por línea);

  2. Elimine los dos últimos de la lista usando head -n -2;

  3. Dado que lsimprime rutas relativas, use el elemento xargs printfpara anteponer la ruta de la carpeta y convertirla en una ruta absoluta;

  4. Envíalos a sudo rm -rfusar xargs.

Todos tienen acceso a esta carpeta, por lo que cualquiera puede crear y eliminar cualquier archivo en esta carpeta.

El problema es: sudo rm -rf da miedo. xargs sudo rm -rfEs increíblemente aterrador.

Quiero asegurarme de que nadie pueda dañar otras carpetas / sistemas creando archivos inteligentes que se eliminen (ya sea accidentalmente o a propósito). No sé, algo inteligente como:

file with / spaces.txt

lo que podría resultar en un súper miedo sudo rm -rf /.

EDITAR: Mi error, los nombres de archivo no pueden contener /, por lo que este problema específico no sucedería, pero la pregunta sobre si existen otros riesgos o no.

Es por eso que estoy usando --quoting-style=shell-always, esto debería evitar cualquier truco con archivos con espacios. Pero ahora me pregunto si alguien podría ser más inteligente con espacios y comillas en el nombre de archivo, tal vez.

¿Es seguro mi script?


Nota: Lo necesito sudoporque estoy accediendo a la carpeta de forma remota (desde una unidad de red asignada usando mount), y no pude hacer que funcione sin sudo.

Pedro A
fuente
3
¿Has considerado hacer algo así printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
steeldriver
¿ /Puedo crear un archivo con el personaje en el nombre? Estoy tratando de lograr esto aquí
George Udosen
3
@ George No, un nombre de archivo no puede contener una barra inclinada.
wjandrea
Entonces, cuando OP dice persona inteligente, me preguntaba ...
George Udosen
66
Simplemente porque está analizando la lssalida, este ya es un comando mal escrito, incluso con comillas. lscreo que también usa la configuración regional para ordenar el orden, así que no veo cuál es el propósito de headeliminar los últimos 2 (a menos que estés tratando de deshacerte .y de ..qué iirc no están permitidos como argumentos de rmtodos modos. Solo usa find /path/to/folder -type f delete. Y no sudosi se ejecuta desde cron - cron ya está en el nivel raíz
Sergiy Kolodyazhnyy

Respuestas:

10

En Linux, cualquier carácter es un nombre de archivo válido que constituye un carácter, excepto:

  • \0 (ASCII NUL): como se usa para la terminación de cadena en C
  • / (barra inclinada): como se usa para la separación del camino

Entonces, su enfoque definitivamente no funcionará en muchos casos, como puede imaginar, por ejemplo, ¿maneja una nueva línea ( \n) en el nombre del archivo? ( Sugerencia: no ).

Pocas notas:

  • No analizar ls; use herramientas dedicadas (hay al menos una para la mayoría de los casos de uso)
  • Al tratar con nombres de archivos, intente aprovechar la salida separada por NUL proporcionada por casi todas las herramientas de GNU que funcionan con dichos datos
  • Tenga cuidado al conectar tuberías, asegúrese de que ambos programas puedan comprender las separaciones NUL
  • Cada vez que invoque xargs, vea si puede salirse con la suya find ... -exec; en la mayoría de los casos, estarás bien findsolo

Creo que esto te pondrá en marcha por ahora. steeldriver ya proporcionó la idea separada por NUL en el comentario ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), use esto como punto de partida.

heemayl
fuente
Gracias por su respuesta :) Creo que debería citar el comentario de steeldriver en lugar de solo mencionarlo (ya que los comentarios no son permanentes). Echaré un vistazo findtambién, gracias por la sugerencia.
Pedro A
Sin embargo, tengo una pregunta: no entiendo lo que quieres decir con "tu enfoque definitivamente no funcionará en muchos casos como te puedes imaginar" - "no funciona" como en "no seguro" o "no inseguro"? Porque "su enfoque" se refiere a mí y no al usuario malintencionado, y sus declaraciones anteriores están a mi favor, por lo que estoy confundido.
Pedro A
@PedroA Eres :) Como dije, como todos los caracteres son válidos, excepto los dos mencionados, deberías poder imaginar los numerosos casos en los que su lsenfoque de análisis podría fallar, por ejemplo, ¿tuvo en cuenta una nueva línea en el nombre del archivo?
heemayl
Oh, una nueva línea en el nombre del archivo ... No había pensado en eso. Si no le importa agregar eso a su respuesta también :) Además, lamento preguntar, pero ¿qué hace head -z? Suena ridículo, pero no tengo manni infoen mi contenedor CoreOS Linux ... Tampoco pude encontrar en Internet. Obtengohead: invalid option 'z'
Pedro A
@PedroA Newline es solo un caso, hay muchos como podría haber adivinado . Necesita GNU head(viene con GNU coreutils). Aquí está la versión en línea: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl
3

xargs admite algunas citas: con comillas simples, comillas dobles o barra diagonal inversa que le permite aceptar argumentos arbitrarios¹, pero con una sintaxis que es diferente de la sintaxis de comillas tipo Bourne.

La implementación de GNU lscomo se encuentra en Ubuntu no tiene ningún modo de cotización que sea compatible con el xargsformato de entrada.

Su ls --quoting-style=shell-alwayses compatible con ksh93, golpe y conchas zsh citando sintaxis, pero sólo cuando la salida del lses interpretado por el shell en el mismo lugar como lsera cuando salida de él. Además, se deben evitar algunos entornos locales, como los que usan BIG5, BIG5-HKSCS, GBK o GB18030.

Entonces, con esos proyectiles, puedes hacer:

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

Pero eso tiene poca ventaja sobre:

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

El único caso en el que se vuelve útil es cuando desea utilizar la -topción de lsordenar los archivos por mtime / atime / ctime o -S/ -V. Pero incluso entonces, también podrías usar zsh:

files=(*(Nom))

por ejemplo, para ordenar los archivos por mtime (use oLfor -Sy nfor -V).

Para eliminar todos menos los dos archivos regulares modificados más recientemente:

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

¹ todavía hay algunas limitaciones de longitud (por execve()y en algunas xargsimplementaciones no GNU arbitrarias mucho más bajas), y algunas xargsimplementaciones no GNU ahogarán la entrada que contiene secuencias de bytes que no forman caracteres válidos.

Stéphane Chazelas
fuente