Grep only Variable Names in Bash script - linux

I have a bash script A, I am writing another script B which prints the variable names which are exported in script A.
Example:
export A=TRUE
export ABC="GFT"
export XYZ="FFF"
\# export P="SSD"
If I execute the script A, it has to give output as follows:
A
ABC
XYZ
I have tried this oneline:
grep export file.env | grep -v "#" | cut -d' ' -f2 | cut -d'=' -f1
But the problem is, if the export is not start from beginning of the line i.e. if there is some space/Tab in the beginning, the above online doesn't work.
Any alternate Solution for this ?

awk '$1 == "export" { split($2, a, "="); print a[1]; }'

A solution using grep:
grep --perl-regexp --only-matching '\s*export\s+([A-Za-z0-9_]+)='

b.sh:
printenv > TMP
. ./a.sh
printenv | diff TMP - | grep -v printenv | sed -n 's/> *\([^ =]*\)=.*/\1/p'
Or to get the changes:
set > TMP
. ./a.sh
set | grep -vf - TMP | cut -d= -f1
this might give you some extra variables that change such as BASH_LINENO.

sed -n '/^[[:blank:]]*export[[:blank:]]*\([^=]\{1,\}\)=.*/ s//\1/p' ScriptA
[[:blank:]] could be replaced by a simple space in most of the case

Related

Linux usernames /etc/passwd listing

I want to print the longest and shortest username found in /etc/passwd. If I run the code below it works fine for the shortest (head -1), but doesn't run for (sort -n |tail -1 | awk '{print $2}). Can anyone help me figure out what's wrong?
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print $2}'
sort -n |tail -1 | awk '{print $2}'
Here the issue is:
Piping finishes with the first sort -n |head -1 | awk '{print $2}' command. So, input to first command is provided through piping and output is obtained.
For the second command, no input is given. So, it waits for the input from STDIN which is the keyboard and you can feed the input through keyboard and press ctrl+D to obtain output.
Please run the code like below to get desired output:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print $2}'
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |tail -1 | awk '{print $2}
'
All you need is:
$ awk -F: '
NR==1 { min=max=$1 }
length($1) > length(max) { max=$1 }
length($1) < length(min) { min=$1 }
END { print min ORS max }
' /etc/passwd
No explicit loops or pipelines or multiple commands required.
The problem is that you only have two pipelines, when you really need one. So you have grep | while read do ... done | sort | head | awk and sort | tail | awk: the first sort has an input (i.e., the while loop) - the second sort doesn't. So the script is hanging because your second sort doesn't have an input: or rather it does, but it's STDIN.
There's various ways to resolve:
save the output of the while loop to a temporary file and use that as an input to both sort commands
repeat your while loop
use awk to do both the head and tail
The first two involve iterating over the password file twice, which may be okay - depends what you're ultimately trying to do. But using a small awk script, this can give you both the first and last line by way of the BEGIN and END blocks.
While you already have good answers, you can also use POSIX shell to accomplish your goal without any pipe at all using the parameter expansion and string length provided by the shell itself (see: POSIX shell specifiction). For example you could do the following:
#!/bin/sh
sl=32;ll=0;sn=;ln=; ## short len, long len, short name, long name
while read -r line; do ## read each line
u=${line%%:*} ## get user
len=${#u} ## get length
[ "$len" -lt "$sl" ] && { sl="$len"; sn="$u"; } ## if shorter, save len, name
[ "$len" -gt "$ll" ] && { ll="$len"; ln="$u"; } ## if longer, save len, name
done </etc/passwd
printf "shortest (%2d): %s\nlongest (%2d): %s\n" $sl "$sn" $ll "$ln"
Example Use/Output
$ sh cketcpw.sh
shortest ( 2): at
longest (17): systemd-bus-proxy
Using either pipe/head/tail/awk or the shell itself is fine. It's good to have alternatives.
(note: if you have multiple users of the same length, this just picks the first, you can use a temp file if you want to save all names and use -le and -ge for the comparison.)
If you want both the head and the tail from the same input, you may want something like sed -e 1b -e '$!d' after you sort the data to get the top and bottom lines using sed.
So your script would be:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n | sed -e 1b -e '$!d'
Alternatively, a shorter way:
cut -d":" -f1 /etc/passwd | awk '{ print length, $0 }' | sort -n | cut -d" " -f2- | sed -e 1b -e '$!d'

How to extract the value before the last underscore in shell script

I need to extract the value before the last underscore in shell script.
Example:
Input: first_second_third_mmddyyy.csv
Output: first_second_third
Input: first_second_mmddyy.csv
Output: first_second
You can use this sed:
sed 's/_[^_]*$//g' file
Test:
$ echo "first_second_third_mmddyyy.csv" | sed 's/_[^_]*$//g'
first_second_third
$ echo "first_second_mmddyy.csv" | sed 's/_[^_]*$//g'
first_second
You can just use shell parameter expansion:
while read -r line; do echo "${line%_*}"; done < file
# ...........................^^^^^^^^^^
You can also use awk or cut as below;
awk -F '_' 'NF{NF-=1};1' file
echo "first_second_third_mmddyyy.csv" | rev | cut -d '_' -f2- | rev
Eg;
$ echo "first_second_third_mmddyyy.csv" | awk -F '_' 'NF{NF-=1};1'
first second third
NF is a variable containing the number of fields in a line.
NF-=1 is subtracting one from the NF variable, to remove last field

Sed to replace tab with Control-A in shell script

Working with data and trying to convert a tab-delimited to control-a in a shell script. Using command-line, I would represent control-a by doing the following sequence, 'control-v, control-a'.
Here is my code in a .sh:
echo "tab delimited query here" | sed 's/ /'\001'/g' > $OUTPUT_FILE
However, this doesn't work. I've also tried the following:
'\x001'
x1
'\x1'
You can use tr here:
echo $'tab\tdelimited\tquery\there' | tr '\t' $'\x01'
To demonstrate that it has been replaced:
echo $'tab\tdelimited\tquery\there' | tr '\t' $'\x01' | cat -vte
Output:
tab^Adelimited^Aquery^Ahere$
sed alternative:
echo $'tab\tdelimited\tquery\there' | sed $'s/\t/\x01/g'
awk alternative:
echo $'tab\tdelimited\tquery\there' | awk -F '\t' -v OFS='\x01' '{$1=$1} 1'

How get value from text file in linux

I have some file xxx.conf in text format. I have some text "disablelog = 1" in this file.
When I use
grep -r "disablelog" oscam.conf
output is
disablelog = 1
But i need only value 1.
Do you have some idea please?
one way is to use awk to print just the value
grep -r "disablelog" oscam.conf | awk '{print $3}'
you could also use sed to replace diablelog = with empty
grep -r 'disablelog' oscam.conf | sed -e 's/disablelog = //'
If you also want to get the lines with or without space before and after = use
grep -r 'disablelog' oscam.conf | sed 's/disablelog\s*=\s*//'
above command will also match
disablelog=1
Assuming you need it as a var in a script:
#!/bin/bash
DISABLELOG=$(awk -F= '/^.*disablelog/{gsub(/ /,"",$2);print $2}' /path/to/oscam.conf)
echo $DISABLELOG
When calling this script, the output should be 1.
Edit: No matter wether there is whitespace or not between the equals sign and the value, the above will handle that. The regex should be anchored in either way to improve performance.
Try:
grep -r "disablelog" oscam.conf | awk -F= '{print $2}'
Just for fun a solution without awk
grep -r disablelog | cut -d= -f2 | xargs
xargs is used here to trim the whitespace

Get N line from unzip -l

I have a jar file, i need to execute the files in it in Linux.
So I need to get the result of the unzip -l command line by line.
I have managed to extract the files names with this command :
unzip -l package.jar | awk '{print $NF}' | grep com/tests/[A-Za-Z] | cut -d "/" -f3 ;
But i can't figure out how to obtain the file names one after another to execute them.
How can i do it please ?
Thanks a lot.
If all you need the first row in a column, add a pipe and get the first line using head -1
So your one liner will look like :
unzip -l package.jar | awk '{print $NF}' | grep com/tests/[A-Za-Z] | cut -d "/" -f3 |head -1;
That will give you first line
now, club head and tail to get second line.
unzip -l package.jar | awk '{print $NF}' | grep com/tests/[A-Za-Z] | cut -d "/" -f3 |head -2 | tail -1;
to get second line.
But from scripting piont of view this is not a good approach. What you need is a loop as below:
for class in `unzip -l el-api.jar | awk '{print $NF}' | grep javax/el/[A-Za-Z] | cut -d "/" -f3`; do echo $class; done;
you can replace echo $class with whatever command you wish - and use $class to get the current class name.
HTH
Here is my attempt, which also take into account Daddou's request to remove the .class extension:
unzip -l package.jar | \
awk -F'/' '/com\/tests\/[A-Za-z]/ {sub(/\.class/, "", $NF); print $NF}' | \
while read baseName
do
echo " $baseName"
done
Notes:
The awk command also handles the tasks of grep and cut
The awk command also handles the removal of the .class extension
The result of the awk command is piped into the while read... command
baseName represents the name of the class file, with the .class extension removed
Now, you can do something with that $baseName

Resources