Estou lendo o livro "sed & awk" de Dougherty e Robbins. Um dos exemplos pede para canalizar a saída para um script de shell:
sed -f nameState list | byState
Mas descobri que, para funcionar, preciso dar ao script 'byState' permissões de execução com chmod e também chamá-lo com ponto-barra:
sed -f nameState list | ./byState
Será que esse sempre será o caso? O livro é de 1997, então talvez o padrão do shell tenha mudado? Ou é porque estou usando bash e não sh ?
EDIT: Aqui está o script byState:
#! /bin/sh
awk -F, ’{
print $4 ", " $0
}’ $* |
sort |
awk -F, ’
$1 == LastState {
print "\t" $2
}
$1 != LastState {
LastState = $1
print $1
print "\t" $2
}’
Executar um nome de comando que não contém um
/
é destinado a comandos que devem ser pesquisados em um dos diretórios listados em$PATH
¹. Você não gostariarm file
de executar orm
comando que por acaso está no diretório de trabalho atual em vez dorm
² padrão.Se você quiser executar o
byState
executável que é encontrado em um diretório específico, em oposição ao primeiro (se houver) encontrado em qualquer um dos$PATH
diretórios, você precisa invocá-lo pelo seu caminho. Como ele precisa conter pelo menos um/
, para aquele que está no diretório de trabalho atual, você normalmente usa./byState
which, comobyState
é um caminho relativo para aquele arquivo, mas é um que contém um/
. Você também pode usar/full/path/to/byState
or././byState
ou../here/byState
which também funcionaria.Se você quiser que esse
byState
script seja executávelbyState
por qualquer pessoa no sistema, independentemente do diretório de trabalho, você pode pedir ao administrador do sistema para colocar uma cópia dele em/usr/local/bin
(ou qualquer diretório que esteja no padrão,$PATH
embora/usr/local/bin
seja o local destinado aos comandos instalados localmente). Ou você pode até mesmo fazer um pacote de software para ele (como um.deb
arquivo se for para um sistema baseado em Debian) incluindo esse executável e sua página de manual, por exemplo, e que listaria suas dependências (sh
,awk
,sort
embora esses sejam softwares essenciais que sempre serão instalados e não precisam ser listados como dependências) e pedir ao administrador para instalá-lo (como comgdebi
/apt
/dpkg
em sistemas baseados em Debian), e então isso poderia ser colocado em/usr/bin
. Isso o tornaria mais limpo, pois seria listado como um software instalado com informações sobre seu mantenedor, onde estão os arquivos correspondentes e facilitaria a desinstalação.Se isso
byState
for só para você, você pode adicioná-lo a um dos seus próprios diretórios e adicionar esse diretório às suas$PATH
variáveis de ambiente. Diretórios típicos para isso são~/bin
ou~/.local/bin
.Em qualquer caso, para poder executar um arquivo, obviamente você precisa ter permissão de execução nele. Para scripts, você também precisa que o arquivo tenha permissão de leitura, pois o interpretador (aqui
/bin/sh
) precisa ser capaz de ler o código nele para executá-lo.Daria a todos (
a
ll)r
ead e ex
ecute permissão para isso. Ou você poderia especificar as permissões na íntegra com:Que dá permissão de leitura+execução para todos, e também permissão de escrita para o proprietário. Ou
chmod 755 byState
com a forma octal.Agora, alguns problemas com seu
sh
script (não é umbash
script, masbash
seria capaz de interpretá-lo, se você perguntasse a ele alterando o shebang para,#! /path/to/bash -
já que sua linguagem tem uma sintaxe compatível):Talvez seja um problema de copiar e colar, mas essas
’
são aspas erradas, essas são U+2019 ASPAS ÚNICAS DIREITAS. As aspas que os shells reconhecem como operadores de aspas fortes são'
, U+0027 APÓSTROPHE.$*
unquoted no contexto de lista não faz sentido algum³.$*
só faz sentido quando citado, mas isso é para juntar os parâmetros posicionais com o primeiro caractere de$IFS
4, o que não é o que você quer aqui. Para passar todos os parâmetros posicionais literalmente paraawk
, você precisa de"$@"
(que deve ser citado).awk
tem esse problema em que se você passar um nome de arquivo comofoo=bar.txt
, ele é tomado como uma atribuição de variável awk. Então aqui, seria melhor fazer 5 :Com
print $4 ", " $0
, você está adicionando um espaço além do,
, então quando nessa saída você fizer,print "\t" $2
, isso imprimirá uma tabulação, espaço e o campo. Provavelmente você quer que o separador de campo de entrada (FS
conforme definido por-F
) e o separador de campo de saída (OFS
) sejam ambos apenas,
.Em
$1 == LastState
, cuidado, issoawk
fará uma comparação numérica se os operandos parecerem números e comparação de strings 6 caso contrário. Por exemplo, ele diria que100
,1e2
e100.0
são idênticos. Se você quiser ter certeza de que uma comparação de strings será realizada, você faria$1 "" == LastState
, ou ao atribuirLastState
, façaLastState = $1 ""
com que ele registre o fato de que é uma string e não potencialmente um número.Em vez de verificar a igualdade duas vezes, com
LastState = $1
andLastState != $1
, você pode adicionar anext
no final da ação para a primeira verificação para pular a necessidade de comparar novamente, ou usar uma instruçãoif
/else
em uma ação executada incondicionalmente.sort
é muito bem capaz de classificar a entrada com base no 4º campo 7 , você não precisa movê-lo para a frente.Ou fatorando
print "\t" $1
e tornando-o mais legível/direto:¹ Para aqueles que não são um comando ou função interna do shell ou alias (quando literal e sem aspas) de uma das palavras-chave na sintaxe do shell, como
while
,for
... (também quando literal e sem aspas).² Décadas atrás, não era incomum incluir
.
ou a string vazia, ambas significando o diretório de trabalho atual em$PATH
, mesmo na frente (!), onde você encontraria esse tipo de problema, mas ninguém seria tolo o suficiente para fazer isso hoje em dia.³ Basicamente, isso é pedir ao shell para juntar os parâmetros posicionais (os argumentos para o script) com o primeiro caractere de
$IFS
4 e então (por causa das aspas faltantes), dividir o resultado novamente em qualquer caractere de$IFS
e sujeitar cada palavra resultante a globbing, também conhecido como geração de nome de arquivo, também conhecido como expansão de nome de caminho . Isso$*
faria sentido norc
shell, o sucessor do Bourne shell, embora isso infelizmente nunca tenha acontecido, mas você precisaria de um#! /bin/rc
shebang, ouzsh
where$*
expande para os parâmetros posicionais não vazios quando não estiver sendo executado na emulaçãosh
/ .ksh
4 Com o Bourne shell, um shell pré-POSIX que ainda era encontrado
/bin/sh
em alguns sistemas em 1997, que os uniria com espaço, independentemente do valor de$IFS
.5
cat
não tem esses problemas, mas (e isso também se aplica asort
eawk
) trata-
como entrada padrão, então se você tiver um arquivo chamado-
, você tem que passá-lo como./
ou qualquer outro caminho para ele para contornar isso.6 Com muitas
awk
implementações, isso nem é uma comparação de string byte a byte, mas se as duas strings são classificadas da mesma forma. Executarawk
no locale C garantiria que isso fosse uma comparação de string.7 A
-k start,end[flag]
sintaxe é a nova (como no início dos anos 90, talvez no final dos anos 80), é possível que em 1997 ainda houvessesort
implementações que não a suportavam e apenas a sintaxe antiga, agora obsoleta+offset
, o que pode explicar por que não a usavam.Sim. Se você vai chamar o script assim, ele precisa ser marcado como executável (e, de preferência, ter um shbang). Isso não é novidade.
Seu termo "ponto-barra" está simplesmente especificando o caminho para o script, o único ponto representando o diretório atual. Como apontado por @ilkachu, é um problema de segurança incluir "ponto" no seu caminho, isso levaria a qualquer diretório que possa estar
$CWD
no momento aparecendo no seu caminho de pesquisa.