Cannot assign output of sed to a variable - shell

I refered this
https://unix.stackexchange.com/questions/251388/prefix-and-suffix-strings-to-each-output-line-from-command
to add prefix to output of ls.
I want to store this output of ls:
file1
file2
file3
in to a variable with value:
/../file1 /../file2 /../file3
This is my .sh file:
PREFIX="/../"
OUTPUT_INLINE=$(ls | tr "\n" " ")
OUTPUT="${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
echo "${OUTPUT_INLINE}"
echo "${OUTPUT}"
Output is:
file1 file2 file3
It means variable OUTPUT contains nothing.
Even if I do:
echo "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
I will get:
/../file1 /../file2 /../file3
What is wrong here ?

You are assigning OUTPUT variable this command
"${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
Which means nothing.
Do as you are already doing with OUTPUT_INLINE variable to assign the output of command.
OUTPUT=$(echo -n "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g")

OUTPUT="${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
That pipes OUTPUT="${OUTPUT_INLINE}" into sed "s|\<|$PREFIX|g", which doesn’t do anything. I think you meant:
OUTPUT=$(printf '%s' "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g")
but there’s lots of fragility here around different delimiter types, and you should be able to avoid all that:
PREFIX="/../"
for filename in *; do
printf '%s%s ' "$PREFIX" "$filename"
done

Related

Echo command containing both double and single quotes

I have this command
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
and at some point, I want to echo it into a file but a naive approach echo command_above doesn't work.
If I put the command into single quotes, then $3 expands to whitespace.
Is it possible to print that command char-by-char as it is after echo command without any expansion and substitution?
The common approach is to use the << operator to read until some delimiter:
# "cat" just prints what it reads
cat << 'EOF' > output_file
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
EOF
Use xargs to pass file names list to cp
ldd MyApp.out | awk '$3!=""{print $3}' | xargs -d'\n' -I{} cp {} lib/
For debugging and logging purposes you can use set -x or set -v:
set -v # dump commands below
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
set +v # stop dumping

how to concatenate the grep output and the indexed string in bash?

I am running a for loop such like
for d in /dir1/dir2/*.txt:
do
cat $d | grep "^ABC" >output.txt
done
My question is how to concatenate both $d and the output for grep? such like
/dir1/dir2/demp1.txt ABCDEFG
/dir1/dir2/demp2.txt ABCD
...
Within your loop store the output of the command in a variable:
e=$(cat ${d} | grep "^ABC")
echo -e "$d\t$e" >> output.txt

Sed replace substring only if expression exist

In a bash script, I am trying to remove the directory name in filenames :
documents/file.txt
direc/file5.txt
file2.txt
file3.txt
So I try to first see if there is a "/" and if yes delete everything before :
for i in **/*.scss *.scss; do
echo "$i" | sed -n '^/.*\// s/^.*\///p'
done
But it doesn't work for files in the current directory, it gives me a blank string.
I get :
file.txt
file5.txt
When you only want the filename, use basename instead of sed.
# basename /path/to/file
returns file
here is the man page
Your sed attempt is basically fine, but you should print regardless of whether you performed a substitution; take out the -n and the p at the end. (Also there was an unrelated syntax error.)
Also, don't needlessly loop over all files.
printf '%s\n' **/*.scss *.scss |
sed -n 's%^.*/%%p'
This also can be done with awk bash util.
Example:
echo "1/2/i.py" | awk 'BEGIN {FS="/"} {print $NF}'
output: i.py
Eventually, I did :
for i in **/*.scss *.scss; do
# for i in *.scss; do
# for i in _hm-globals.scss; do
name=${i##*/} # remove dir name
name=${name%.scss} # remove extension
name=`echo "$name" | sed -n "s/^_hm-//p"` # remove _hm-
if [[ $name = *"."* ]]; then
name=`echo "$name" | sed -n 's/\./-/p'` #replace . to --
fi
echo "$name" >&2
done

using awk within loop to replace field

I have written a script finding the hash value from a dictionary and outputting it in the form "word:md5sum" for each word. I then have a file of names which I would like to use to place each name followed by every hash value i.e.
tom:word1hash
tom:word2hash
.
.
bob:word1hash
and so on. Everything works fine but I can not figure out the substitution. Here is my script.
$#!/bin/bash
#/etc/dictionaries-common/words
cat words.txt | while read line; do echo -n "$line:" >> dbHashFile.txt
echo "$line" | md5sum | sed 's/[ ]-//g' >> dbHashFile.txt; done
cat users.txt | while read name
do
cat dbHashFile.txt >> nameHash.txt;
awk '{$1="$name"}' nameHash.txt;
cat nameHash.txt >> dbHash.txt;
done
the line
$awk '{$1="$name"}' nameHash.txt;
is where I attempt to do the substitution.
thank you for your help
Try replacing the entire contents of the last loop (both cats and the awk) with:
awk -v name="$name" -F ':' '{ print name ":" $2 }' dbHashFile.txt >>dbHash.txt

results of wc as variables

I would like to use the lines coming from 'wc' as variables. For example:
echo 'foo bar' > file.txt
echo 'blah blah blah' >> file.txt
wc file.txt
2 5 23 file.txt
I would like to have something like $lines, $words and $characters associated to the values 2, 5, and 23. How can I do that in bash?
In pure bash: (no awk)
a=($(wc file.txt))
lines=${a[0]}
words=${a[1]}
chars=${a[2]}
This works by using bash's arrays. a=(1 2 3) creates an array with elements 1, 2 and 3. We can then access separate elements with the ${a[indice]} syntax.
Alternative: (based on gonvaled solution)
read lines words chars <<< $(wc x)
Or in sh:
a=$(wc file.txt)
lines=$(echo $a|cut -d' ' -f1)
words=$(echo $a|cut -d' ' -f2)
chars=$(echo $a|cut -d' ' -f3)
There are other solutions but a simple one which I usually use is to put the output of wc in a temporary file, and then read from there:
wc file.txt > xxx
read lines words characters filename < xxx
echo "lines=$lines words=$words characters=$characters filename=$filename"
lines=2 words=5 characters=23 filename=file.txt
The advantage of this method is that you do not need to create several awk processes, one for each variable. The disadvantage is that you need a temporary file, which you should delete afterwards.
Be careful: this does not work:
wc file.txt | read lines words characters filename
The problem is that piping to read creates another process, and the variables are updated there, so they are not accessible in the calling shell.
Edit: adding solution by arnaud576875:
read lines words chars filename <<< $(wc x)
Works without writing to a file (and do not have pipe problem). It is bash specific.
From the bash manual:
Here Strings
A variant of here documents, the format is:
<<<word
The word is expanded and supplied to the command on its standard input.
The key is the "word is expanded" bit.
lines=`wc file.txt | awk '{print $1}'`
words=`wc file.txt | awk '{print $2}'`
...
you can also store the wc result somewhere first.. and then parse it.. if you're picky about performance :)
Just to add another variant --
set -- `wc file.txt`
chars=$1
words=$2
lines=$3
This obviously clobbers $* and related variables. Unlike some of the other solutions here, it is portable to other Bourne shells.
I wanted to store the number of csv file in a variable. The following worked for me:
CSV_COUNT=$(ls ./pathToSubdirectory | grep ".csv" | wc -l | xargs)
xargs removes the whitespace from the wc command
I ran this bash script not in the same folder as the csv files. Thus, the pathToSubdirectory
You can assign output to a variable by opening a sub shell:
$ x=$(wc some-file)
$ echo $x
1 6 60 some-file
Now, in order to get the separate variables, the simplest option is to use awk:
$ x=$(wc some-file | awk '{print $1}')
$ echo $x
1
declare -a result
result=( $(wc < file.txt) )
lines=${result[0]}
words=${result[1]}
characters=${result[2]}
echo "Lines: $lines, Words: $words, Characters: $characters"

Resources