CLI do Windows equivalente a "sort-u"
Eu preciso de uma forma equivalente em um CLI do Windows para fazer o que eu seria capaz de fazer em Linux/UNIX em piping para mostrar valores únicos.
Não me parece que haja uma ordem como essa, pelo que consigo perceber, então há outra maneira de fazer isto? o que tenho de conseguir é listar ficheiros de várias pastas (quer existam ou não, o que faço actualmente com dir
). A forma como o script (para a aplicação requerente) funciona usa várias fontes para construir um lista de diretório e, posteriormente, o comando, dependendo se a plataforma é Windows ou UNIX, mas o perigo aqui é que há uma possibilidade de diretórios duplicados na lista e isso alteraria os resultados no final.
a maneira mais fácil de lidar com isto é fazê-lo na fonte, ou seja, o comando original a ser executado. Assim, no Linux, a estrutura de comandos é mais ou menos:
find [dir] [dir] [dir] | grep file_name | sort -u
Fazer o mesmo nas janelas é obviamente mais difícil, dado que:
- it tem de ser um texto nativo apenas para leitura de comandos/script que usaria na linha de comandos do Windows (
cmd.exe
) - não consigo instalar nada nos anfitriões em que isto seja executado
- não posso criar ficheiros de texto temporários como parte do processo
3 answers
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
SET "line="
FOR /f "delims=" %%a IN ('sort q22351713.txt') DO (
IF "!line!" neq "%%a" ECHO(%%a
set "line=%%a"
)
)>newfile.txt
GOTO :EOF
Usei um ficheiro chamado q22351713.txt
para os meus testes.
Produz um ficheiro Novo.txt
NÃO à prova de bala
É sempre melhor explicar o que o original faz em vez de assumir que é óbvio.%COMSPEC% /d /v:on /c "(SET LASTLINE=) & (FOR /f "usebackq tokens=*" %L IN (`DIR /b /s /-p dir1 dir2 dir3 dir3 dir2 dir1 "dir with space" ^| %SYSTEMROOT%\system32\find.exe /i "search_file_name" ^| %SYSTEMROOT%\system32\sort.exe`) DO @((IF /i NOT "{!LASTLINE!}"=="{%~L}" ECHO %~L) & SET "LASTLINE=%~L"))"
Vamos dividir isto um pouco...
%COMSPEC% /d /v:on /s /c "..."
- você queria um único comando, não um lote, e precisávamos de expansão atrasada (/v:on
), por isso invocamos uma subtileza usando o executável conhecido da linha de comandos Windows (%COMSPEC%
). Não queremos executar autoexec.bat (/d
) e queremos executar o comando e seguir em frente, em vez de deixar uma shell interactiva (/c "..."
). Citando é um problema funky, então nós tentamos para algum comportamento "padronizado" (/s
)
SET LASTLINE=
- esvaziar qualquer conteúdo actual de uma variável de ambiente LASTLINE
(...) & (...)
- execute tudo antes de &
e tudo depois de &
independentemente do código de saída da primeira parte. Usar parêntesis conforme apropriado para agrupar comandos, quando necessário.
FOR /f "usebackq tokens=*" %L IN (`...`) DO ...
- execute a cadeia de comandos entre as fichas (usebackq
) e leia todas as palavras (tokens=*
) em cada linha de stdout (FOR /f
) numa variável temporária %L
.
DIR /b /s /-p dir1 dir2 dir3 dir3 dir2 dir1 "dir with space"
- enumerar recursivamente (/s
através dir1
, dir2
, dir3
e dir with space
, imprimindo apenas o caminho completo dos ficheiros para o stdout (/b
). Não necessite de teclas para page o resultado, se este tiver sido definido (/-p
). Note que, devido a foibles de FOR
, depende de si apenas pastas de aspas com caracteres especiais (por exemplo, espaço), ou seja, não cite nomes de uma única palavra.
... ^| ...
- encaminhe o stdout do primeiro comando para o stdin do segundo. Nós usamos ^
para escapar |
dentro dos backquotes de FOR
.
%SYSTEMROOT%\system32\find.exe /i "search_file_name"
- encontre o texto search_file_name
em stdin. Usar uma pesquisa sem distinção de maiúsculas (/i
).
%SYSTEMROOT%\system32\sort.exe
- separem o stdin e escrevam ao stdout.
DO @(...)
- temos de executar dois comandos para cada linha, por isso agrupamo-los entre parêntesis. {[25] } normalmente imprime cada comando antes de o executar, o @
suprime isto.
(IF /i NOT "{!LASTLINE!}"=="{%~L}" ECHO %~L)
- Se a linha que estamos olhando %~L
não é a mesma que a linha que armazenamos !LASTLINE!
, imprima a linha que estamos olhando. Mais uma vez, use um comparação insensível à capitalização (/i
). Reservamos ambas as variáveis com {}
para lidar com valores vazios, e usamos expansão retardada (!LASTLINE!
em vez de %LASTLINE%
) para expandir LASTLINE
cada vez que ele faz loops, não apenas quando o comando FOR
é visto pela primeira vez.
SET "LASTLINE=%~L"
- armazenar a linha que estamos olhando %~L
na variável LASTLINE
.
Se não tiver acesso às variáveis de ambiente incorporadas, pode não ter %SYSTEMROOT%
ou %COMSPEC%
. Substituir estes por C:\Windows
e C:\Windows\System32\cmd.exe
ou mais apropriadamente, determine os locais corretos para o sistema que você está digitalizando.
-
A Microsoft adicionou uma opção
/unique
parasort.exe
no Windows 10, mas esqueceu-se de actualizar a sua documentação. Testei este interruptor no Windows 7, no Windows 8.1 e no Windows 10 v1709. Estava presente no último, mas ausente dos dois primeiros.So,
sort.exe /unique
is the equivalent of UNIXsort -u
. O PowerShell já tem um cmdlet
Sort-Object
que suporta um interruptor-Unique
. Melhor ainda, por padrão, PowerShell define a alcunhasort
paraSort-Object
Assim,| sort -unique
é é tudo o que precisas.