Unix Shell script code meaning for beginner - shell

I am a novice learner of Unix and shell scripting.
Can anyone explain the meaning of this line and how it works:
Record_count=$(wc -l ${table_dir} "/" $table_file_name | cut -d' ' f1)
I am not sure of what "/" does here.

Let's go step by step.
First step. This wc -l ${table_dir} "/" $table_file_name doesn't work as it's written but I understand it means return the number of lines (wc -l) of the file ${table_dir}/${table_file_name}. It returns something that looks like this (imagining that your_table_file_name.txt has 5 lines):
$ wc -l wc -l "${table_dir}/${table_file_name}"
5 your_table_dir/your_table_file_name.txt
Second step. I think this cut -d' ' f1 has a typo and is actually cut -d ' ' -f1. What this does is splitting a line by the space character (cut -d ' ') and only returns the first item of the sequence (-f1).
So, when you apply it to your line 5 your_table_dir/your_table_file_name.txt, it returns 5.
Third step. So what wc -l "${table_dir}/${table_file_name}" | cut -d ' ' -f1 does is returning the number of lines that ${table_dir}/${table_file_name} has.
Final step. In shell script, foo=$(some_command) means: assign to the variable called foo, the result of the command some_command.
So, what your whole line Record_count=$(wc -l "${table_dir}/${table_file_name}" | cut -d ' ' -f1) does is assigning to the variable Record_count, the count of the lines of the file ${table_dir}/${table_file_name}.

Related

Can I catch the value by using bourne shell script?

When I type some command in openwrt, the result is like this.
Security Signal(%) Mode
WPA2 86 on
WPA2 42 on
In this result, I want to catch the signal value(86) in first column.
How can i catch the value by using bourne shell script?
Plus, luci.sys.call function is only used in cbi file for making Luci, isn't it?
Try also
HereYourCommand | awk 'NR==2 { print $2 }'
The awk program prints the second field (aka column) of the second record (aka line).
The following should do:
HereYourCommand | head -2 | tail -1 | tr -s ' ' | cut -d' ' -f2
Replace HereYourCommand with your call to openwrt.
The explanation:
head -2: pick up just the first two lines.
tail -1: from this two lines, pick up the last line.
tr -s ' ': replace multiple spaces with a single one.
cut -d' ' -f2: pick up the 2nd field from the remaining line.
cat test|tail +2|tr -s '\s\t' ' '|cut -d' ' -f2
tail +2 skips first line, then I am replacing spaces or tabs with single space and cut get second field.
output:
4
5
8
input:
x y z
1 4 7
2 5 7
4 8 0

Is it possible to set variable in pipeline?

I have a big txt file which I want to edit in pipeline. But on same place in pipeline I want to set number of lines in variable $nol. I just want to see sintax how could I set variable in pipeline like:
cat ${!#} | tr ' ' '\n'| grep . ; $nol=wc -l | sort | uniq -c ...
That after second pipe is very wrong, but how can I do it in bash?
One of solutions is:
nol=$(cat ${!#} | tr ' ' '\n'| grep . | wc -l)
pipeline all from the start again
but I don't want to do script the same thing twice, bec I have more pipes then here.
I musn't use awk or sed...
You can use a tee and then write it to a file which you use later:
tempfile="xyz"
tr ' ' '\n' < "${!#}" | grep '.' | tee > "$tempfile" | sort | uniq -c ...
nol=$(wc -l "$tempfile")
Or you can use it the other way around:
nol=$(tr ' ' '\n' < "${!#}" | grep '.' \
| tee >(sort | uniq -c ... > /dev/tty) | wc -l
You can set a variable in a particular link of a pipeline, but that's not very useful since only that particular link will be affected by it.
I recommend simply using a temporary file.
set -e
trap 'rm -f "$tmpf"' EXIT
tmpf=`mktemp`
cat ${!#} | tr ' ' '\n'| grep . | sort > "$tmpf"
nol="$(wc "$tmpf")"
< "$tmpf" uniq -c ...
You can avoid the temporary file with tee and a named pipe, but it probably won't perform much better (it may even perform worse).
UPDATE:
Took a minute but I got it...
cat ${!#} | tr ' ' '\n'| tee >(nol=$(wc -l)) | sort | uniq -c ...
PREVIOUS:
The only way I can think to do this is storing in variables and calling back. You would not execute the command more than one time. You would just store the output in variables along the way.
aCommand=($(cat ${!#} | tr ' ' '\n'));sLineCount=$(echo ${#aCommand[#]});echo ${aCommand[#]} | sort | uniq -c ...
aCommand will store the results of the first set of commands in an array
sLineCount will count the elements (lines) in the array
;... echo the array elements and continue the commands from there.
Looks to me like you're asking how to avoid stepping through your file twice, just to get both word and line count.
Bash lets you read variables, and wc can produce all the numbers you need at once.
NAME
wc -- word, line, character, and byte count
So to start...
read words line chars < <( wc < ${!#} )
This populates the three variables based on input generated from process substitution.
But your question includes another partial command line which I think you intend as:
nol=$( sort -u ${!#} | wc -l )
This is markedly different from the word count of your first command line, so you can't use a single wc instance to generate both. Instead, one option might be to put your functionality into a script that does both functions at once:
read words uniques < <(
awk '
{
words += NF
for (i=1; i<=NF; i++) { unique[$i] }
}
END {
print words,length(unique)
}
' ${!#}
)

Match List of Numbers in For Loop in Bash

I have a script that loops over a curl command, which pulls in data from an API.
LIST_OF_ID=$(curl -s -X POST -d "username=$USER&password=$PASS&action=action" http://link.to/api.php)
for PHONE_NUMBER in $(echo $LIST_OF_ID | tr '_' ' ' | awk '{print $2}');
do
$VOIP_ID = $(echo $LIST_OF_ID | tr '_' ' ' | awk '{print $1}')
done
I also have a variable of 16 numbers in the range of "447856321455"
NUMBERS=$(cat << EOF
441111111111
441111111112
441111111113
... etc
)
The output on the API call is:
652364_441111111112
As you may notice I have taken the output and cut it into 2 parts and put it in a variable.
What I need is to match the 6 digit code from the output where the number in the output, matches with the number in the variable.
I've attempted it using if statements but I can't work my head around the correct way of doing it.
Any help would be appreciated.
Thank you.
I would do it using join rather than a loop in bash. Like this:
curl -s -X POST -d "$PARAMS" "$URL" | sort \
| join -t _ -2 2 -o 2.1 <(sort numbers.txt) -
What this does is take the sorted output from curl and join it with the sorted contents of numbers.txt (you could use $NUMBERS too), using _ as the separator, using column 2 of file 2 which is - meaning stdin (from curl). Then output field 2.1 which is the six-digit ID.
Read why-is-using-a-shell-loop-to-process-text-considered-bad-practice and then do something like this:
curl ... |
awk -v numbers="$NUMBERS" -F'_' '
BEGIN { split(numbers,tmp,/[[:space:]]+/); for (i in tmp) nums[tmp[i]] }
$2 in nums
'
but to be honest I cant really tell what it is you are trying to do as the numbers in your sample input don't seem to match each other (what does in the range of "447856321455" mean and how does it relate to $NUMBERS containing 441111111111 through 441111111113 and how does any of that relate to match the 6 digit code) and the expected output is missing.

Zsh history as array

For some reason, I can't, for the life of me, get zsh to produce an array containing one line from the entire shell history per element. (i.e. hist_arr[1] == $(history 1 1 | tr -s " " | cut -d ' ' -f 3-), hist_arr[2] == $(history 2 2 | tr -s " " | cut -d ' ' -f 3-), ... <for ten thousand lines>). I'd like to compute the whole array in a single step, so it's more efficient.
hist_arr[1]=$(history 1 1) works fine, but contains redundant history number.
If that is your problem then simple remove it, eg. this way:
hist_arr[1]=$(history 1 1 | tr -s " " | cut -d ' ' -f 3-)
Edit:
If you want to assign to table all element from history file then
IFS=$'
'
hist_arr=( $(awk 'BEGIN{FS=OFS=";"} {$1=""; sub(/\;/, "")}'1 .zsh_history) )
should work.

what does the line of code mean out of curiosity

JUst out of curiosity, can anyone tell me what do 1 does at the end of this statement?
md5 = $(md5sum ${my_iso_file} | cut -d ' ' -f 1)
-f is a "field-list" option for the cut command. The 1 is the value provided for that option, meaning that cut should only print field 1.
Source: http://www.ss64.com/bash/cut.html
it gives out the md5sum of an iso file; md5sum gives out
<md5sum> <filename>
and the cut returns the first word

Resources