AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 794236
Accepted
einpoklum
einpoklum
Asked: 2025-04-27 17:08:57 +0800 CST2025-04-27 17:08:57 +0800 CST 2025-04-27 17:08:57 +0800 CST

Uma maneira idiomática de gerar um nome de arquivo exclusivo?

  • 772

Em um script que estou escrevendo, quero criar algo temporário no meu sistema de arquivos, mas não está lá, /tmpmas em outro lugar, e pode não ser um arquivo nem um diretório (por exemplo, talvez seja um pipe nomeado ou um link simbólico). A questão é que terei que criar tudo sozinho. Agora, quero usar um nome de arquivo exclusivo para o meu temporário, para que futuras invocações do utilitário, além de qualquer outro código em execução, não tentem usar o mesmo nome.

Se eu estivesse apenas criando um arquivo ou diretório temporário em /tmp, eu poderia usar mktemp. Mas o que eu faço quando quero apenas gerar o nome?

shell-script
  • 6 6 respostas
  • 445 Views

6 respostas

  • Voted
  1. Marcus Müller
    2025-04-27T17:32:28+08:002025-04-27T17:32:28+08:00

    Um requisito um tanto conflitante! Em termos linguísticos , você coloca arquivos exclusivos em $TMPDIR(ou /tmp por padrão), ou eles são arquivos portadores de estado , e nesse caso eles pertencem a $XDG_STATE_HOME/yourapplicationname/(que por padrão é $HOME/.local/state/yourapplicationname/).

    A questão é: eu mesmo farei a criação.

    Essa é uma péssima ideia! Deixe mktempo arquivo ser criado e, em seguida, sobrescreva-o com o que você quiser. Deixar que mktempo arquivo seja criado garante que você esteja usando algo único que nunca foi usado antes¹, por exemplo, por uma execução interrompida do seu script ou por um segundo script.

    Em ambos os casos, você usamktemp , com a opção -p, por exemplo, algo como

    filename="$(mktemp -p "${XDG_STATE_HOME:-$HOME/.local/state}/einpoklumsscript/")"
    

    Mas esse é um nome de arquivo temporário seguro, e não é exatamente o mesmo que um nome de arquivo único. Para isso, normalmente você precisaria de um UUID (identificador universal único) e pode gerar um usando uuidgen. Mas: ao contrário de mktemp, isso não é seguro para raça. Parece que você quer mktemp , de verdade!


    ¹ Para seu interesse técnico, por que esta é provavelmente a única maneira de criar um nome de arquivo seguro contra colisões, mesmo ao executar várias instâncias do seu script:

    mktempcria esse arquivo temporário chamando openat(AT_FDCWD, "/path/to/random/filename", O_RDWR|O_CREAT|O_EXCL).

    E a parte emocionante aqui é O_RDWR|O_CREAT|O_EXCL:

    • O_RDWRgarante que o arquivo só será criado se o resultado puder ser lido e gravado.
    • O_CREAT permite a criação do arquivo abrindo-o,
    • O_EXCL impõe que este arquivo seja criado; se ele existia antes, openatfalha e mktempescolhe um nome de arquivo diferente.

    A API do sistema de arquivos UNIX garante que duas tarefas, mesmo em execução exatamente síncrona, não possam O_EXCLabrir o mesmo nome de arquivo – portanto, essa é a única maneira fornecida pelo sistema operacional de garantir que um arquivo seja criado apenas uma vez. Qualquer outra coisa é uma condição de corrida!

    Então, tanto corretamente quanto em termos idiomáticos : use mktemppara criar um arquivo temporário no qual você então escreve. Essa é a maneira UNIX de fazer! Não crie você mesmo.

    (um link simbólico no meu caso, ou um pipe nomeado, ou algo similar) no meu sistema de arquivos, […] Eu mesmo terei que fazer a criação.

    Felizmente, isso não é totalmente verdade. Além disso openat(… , O_CREAT|O_EXCL), a chamada de sistema tambémrenameat é atômica.

    Então, a dança corrida-condição-segurança se torna, se você estiver em um script de shell:

    1. filename="$(mktemp -p "${directory}")"para "reservar" o nome do arquivo, com segurança
    2. substitua esse arquivo pelo que você deseja criar, atomicamente:
      • Se o que você quer substituir for um link simbólico, ln -f -s -- whatever "${filename}"faça isso corretamente, atomicamente (criando um link simbólico com nome aleatório e renameataplicando - a ele ${filename}). Suspeito que seja isso que você quer aqui.
      • se o que você deseja substituir for um pipe nomeado (ou qualquer outra coisa para a qual a ferramenta de criação não permita forçar a substituição):
        primeiro, você cria um diretório temporário local com segurança no mesmo sistema de arquivos, localtmp="$(mktemp -d -p "${directory}")",
        então você cria um pipe nomeado lá, mkfifo -- "${localtmp}/fifo",
        então você o renomeia para o destino mv -- "${localtmp}/fifo" "${filename}"e,
        finalmente, você remove o diretório temporário vaziormdir -- "${directory}"

    Pronto! É verdade que dá um pouco mais de trabalho, mas é assim que você pode nomear qualquer coisa aleatoriamente em um script de shell, então valeu a pena anotar :)

    • 10
  2. Best Answer
    ilkkachu
    2025-04-27T21:08:45+08:002025-04-27T21:08:45+08:00

    Bem, você quer um nome único garantido ou um nome probabilisticamente único? Se for a segunda opção, então sim, gere um nome aleatório usando qualquer significado, use-o e continue com o trabalho. Mas isso deixa a possibilidade, por menor que seja, de que outra cópia da mesma ferramenta (ou até mesmo uma não relacionada) possa acabar usando o mesmo arquivo, se por acaso gerar exatamente o mesmo nome.

    Para garantir a unicidade, você precisa criar o objeto e verificar se ele foi criado novamente. O sistema operacional pode fazer isso com segurança, desde que você solicite, por exemplo, a open()chamada de sistema com os sinalizadores O_CREATand O_EXCLfalha se um arquivo com o mesmo nome já existir. O mesmo vale para mkfifo()and mkdir().

    Então, se você quiser um FIFO, pode gerar um nome, executar mkfifouma verificação se for bem-sucedido e tentar novamente com um novo nome se falhar. Mas fazer isso manualmente é um pouco cansativo. Para arquivos comuns, ele mktempfaz isso por você, mas não cria FIFOs.

    No entanto, ele pode criar um diretório exclusivo para você, e então você pode criar quaisquer arquivos/links simbólicos/FIFOs dentro desse diretório, sabendo que todo o diretório tem um nome exclusivo.

    Então:

    dir=/path/to/worktree/
    d=$(mktemp -d -- "$dir/tmp.XXXXXX") || exit
    fifo="$d/myfifo"
    if ! mkfifo -- "$fifo"; then
        # this shouldn't happen
        printf >&2 '%s\n' "mkfifo '$fifo' failed??"
        exit 1
    fi
    
    # ...
    
    rm -f -- "$fifo" && rmdir -- "$d"
    

    Se você também não conseguir criar o diretório intermediário, precisará aceitar a possibilidade de uma colisão ou fazer a verificação manualmente:

    dir=/path/to/worktree/
    until
        fifo=$(mktemp -u -- "$dir/myfifo.XXXXXX") || exit
        mkfifo -- "$fifo"
    do
        echo >&2 "oops, collision, retrying..."
    done
    printf '%s\n' "created '$fifo'"
    
    • 6
  3. Bob Goddard
    2025-04-27T17:16:50+08:002025-04-27T17:16:50+08:00

    Da mktemppágina do manual...

    -u, --dry-run
     do not create anything; merely print a name (unsafe)
    

    E você não pode garantir o futuro.

    • 4
  4. Chester Gillon
    2025-04-27T17:27:20+08:002025-04-27T17:27:20+08:00

    mktemptem a seguinte opção:

           -u, --dry-run
                  do not create anything; merely print a name (unsafe)
    

    Por exemplo, obtive apenas um caminho temporário sem criação usando:

    [mr_halfword@skylake-alma eclipse_project]$ mktemp --dry-run --tmpdir=$HOME
    /home/mr_halfword/tmp.luj0friP22
    [mr_halfword@skylake-alma eclipse_project]$ file /home/mr_halfword/tmp.luj0friP22
    /home/mr_halfword/tmp.luj0friP22: cannot open `/home/mr_halfword/tmp.luj0friP22' (No such file or directory)
    

    A questão é: eu mesmo farei a criação.

    O motivo pelo qual a --dry-runopção não é segura é que ela não garante que dois processos não tentem usar o mesmo caminho temporário.

    Se eu estivesse apenas criando um arquivo ou diretório temporário em /tmp, eu poderia usar mktemp.

    Considerando que a --tmpdiropção especifica o diretório temporário, substituindo o padrão de /tmp, pode ser mais seguro usá-la --tmpdirpara permitir mktempa criação de um caminho temporário de sua escolha de forma segura.

    • 4
  5. Greg A. Woods
    2025-04-28T04:37:02+08:002025-04-28T04:37:02+08:00

    A verdadeira maneira idiomática (no Unix) de gerar um nome de arquivo temporário exclusivo é usar o ID do processo atual para formar o nome.

    Por exemplo, em um script de shell:

    touch tmpfile.$$
    

    Só pode haver um processo com um determinado ID em execução ao mesmo tempo, portanto o ID do processo é exclusivo durante toda a vida útil desse processo.

    Em geral, isso costuma ser considerado como tendo "singularidade" suficiente para qualquer arquivo temporário , pois a maioria desses arquivos terá apenas uma vida útil igual à do processo que os cria e os utiliza.

    De fato, se o seu arquivo for realmente temporário e for usado apenas enquanto o seu processo (script) estiver em execução, mesmo que haja um arquivo obsoleto com o mesmo nome, você pode usá-lo como se tivesse sido criado pelo seu processo, pois é impossível que o processo anterior que o criou ainda o esteja usando. Esse processo não está mais em execução, pois o seu processo atual agora tem o mesmo ID de processo e, portanto, esse arquivo, mesmo que existisse anteriormente, ainda é "único", embora talvez precise de alguma limpeza, como truncamento, se for um arquivo comum.

    Claro que se um determinado usuário só puder executar uma instância do seu script por vez, e ele o executar de forma que o arquivo temporário seja criado em um diretório privado onde somente ele poderá gravar, então incluir o ID do processo no nome do arquivo temporário não faz sentido, mas as pessoas provavelmente considerarão seu código suspeito se você não incluir o ID do processo.

    O restante disto é uma digressão sobre possíveis problemas de segurança que podem ou não afetar seu script:

    Usar um nome "previsível", por exemplo, um que depende do ID do processo para exclusividade, nem sempre é considerado seguro, especialmente se o diretório onde o arquivo é criado tiver mais permissões do que deveria (ou for um diretório compartilhado com permissões intencionalmente não privadas, por exemplo /tmp), pois algum invasor pode ter criado o arquivo de destino com permissões ligeiramente diferentes das que você pretende, ou até mesmo como um link simbólico apontando para um arquivo sensível, etc., etc., etc.

    Já que você está perguntando sobre scripts de shell, a maneira mais segura de garantir que um arquivo de algum tipo arbitrário seja único e criado com segurança como único é criá-lo em um diretório com nome exclusivo, já que diretórios podem ser criados com segurança com nomes exclusivos a partir de um script de shell (e presumivelmente com permissões privadas, desde que uma senha segura umaskesteja definida). Dessa forma, seu script também pode evitar com segurança potenciais ataques "Time of Check(creation) vs. Time Of Use" (TOCTOU). Basta adicionar um número de "edição" ao caminho se o mkdir(1)erro falhar e tentar novamente. Ainda existe o risco de um invasor realizar um ataque de negação de serviço contra seu script...

    Então, é claro que a expressão moderna é de fato usar mktemp(1), com sua -dopção para seus propósitos, já que isso lhe dará um umaskambiente seguro, protegido e, claro, único e privado (mesmo com um ) inseguro para criar qualquer tipo de arquivo que desejar. Você pode evitar isso /tmpusando a -p diropção , onde, claro, dir pode ser apenas " ." se for o que você precisa. mktemp(1)cuida para garantir que o resultado seja único e não entre em conflito com quaisquer sobras obsoletas.

    • 1
  6. j0h
    2025-04-28T02:30:04+08:002025-04-28T02:30:04+08:00

    Nenhuma frase de efeito? Inacreditável!

    echo $(head /dev/urandom | tr -dc A-Za-z0-9 | head -c6)
    

    -c66 letras ou 6^62 possibilidades.

    • 0

relate perguntas

  • Subtraindo a mesma coluna entre duas linhas no awk

  • Um script que imprime as linhas de um arquivo com seu comprimento [fechado]

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Dividir por delimitador e concatenar problema de string

  • MySQL Select com função IN () com array bash

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve