issue with using double grave accent (backtick) in bash - bash

I am trying to figure out which files I need to modify; so by that, I use sequences of grep commands. I want to find out which files contain both foo and bar. Therefore, my command is:
grep foo `grep bar * -l` | awk -F':' '{print $1}' | sort | uniq
This command gets me a big list that looks like this:
pageABC.txt
pageBCD.txt
pageDEF.txt
I want this output to be opened in emacs. So what I'd normally do is:
emacs ` whatever_was_in_my_output `
This command normally opens all the files.
If I try
emacs `grep foo `grep bar * -l` | awk -F':' '{print $1}' | sort | uniq `
Emacs won't even start. Maybe it's because of the double grave accents used.
Any ideas how to solve this?
Many Thanks,
D

You forgot to escape the inner command substitution:
emacs `grep foo \`grep bar * -l\` | awk -F':' '{print $1}' | sort | uniq`
In cases like this, I usually prefer the alternative command substition syntax, since it nests more easily:
emacs $(grep foo $(grep bar * -l) | awk -F':' '{print $1}' | sort | uniq)

Avoid backticks in bash, and use $(command) to run sub-commands. They nest properly, unlike backticks.

Related

Bash, how to create an array in one line of code

how can I create an array in one step instead of two stages, like shown below?'
The example below was executed on a live Linux system.
POSITION=`volt |grep ate |awk '{print $4}'` #returns three integers
declare -a POSITION_ARRAY=($POSITION) #create an array
You don't need the intermediate variable, as wjandrea said. These two snippets are equivalent:
POSITION=$(volt | grep ate | awk '{print $4}')
declare -a POSITION_ARRAY=($POSITION)
# declare -a also works, but isn't needed in modern Bash
POSITION_ARRAY=( $(volt | grep ate | awk '{print $4}') )
If you know the output of the pipeline is witespace-delimited integers this will do what you want. But it isn't a safe way to populate an array from arbitrary command output, because unquoted expansions will be word-split and globbed.
The proper way to read a command's output into an array, split by lines, is with the readarray builtin, like so:
readarray -t POSITION_ARRAY < <(volt | grep ate | awk '{print $4}')
Simply put the command in the parentheses.
By the way, declare -a is not needed, and backticks are deprecated in favour of $().
POSITION_ARRAY=( $(volt | grep ate | awk '{print $4}') )
And FWIW you can merge the grep and AWK commands:
POSITION_ARRAY=( $(volt | awk '/ate/ {print $4}') )

Rewrite bash command to be ShellCheck compliant

How can I rewrite the following bash command to be ShellCheck compliant?
memory=$(cat /proc/meminfo | grep 'MemTotal:' | awk {'print $2}' 2> /dev/null)
It is currently complaining about:
Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..'
This { is literal.
The shellcheck complaints are
Using cat filename | grep 'pattern' instead of grep 'pattern' filename
The first brace in the awk command is on the outside of the single quote, hence literal; it should be awk '{command}'
So, a version that would satisfy shellcheck would look like
memory=$(grep 'MemTotal:' /proc/meminfo | awk '{print $2}')
I'm not sure why you redirected standard error, so I dropped it.
However, piping grep output to awk is rarely the best solution; awk can do almost anything grep can, so you could further simplify to
memory=$(awk '/MemTotal:/ { print $2 }' /proc/meminfo)
No pipes!
Just as a guess, you probably want:
memory=$(grep 'MemTotal:' /proc/meminfo | awk '{print $2}' 2> /dev/null)
However, that's a direct interpretation of the shellcheck output, so I am unsure as to what you are asking about. As it stands, you are essentially asking: "Why isn't this working?".

awk issue, summing lines in various files

I have a list of files starting with the word "output", and I want to sum up the total number of rows in all the files.
Here's my strategy:
for f in `find outpu*`;do wc -l $f | awk '{x+=$1}END{print $1}' ; done
Before piping over, if there were a way I could do something like >> to a temporary variable and then run the awk command after, I could accomplish this goal.
Any tips?
use this to see details and sum :
wc -l output*
and this to see only the sum:
wc -l output* | tail -n1 | cut -d' ' -f1
Here is some stuff for fun, check it out:
grep -c . out* | cut -d':' -f2- | paste -sd+ | bc
all lines, including empty ones:
grep -c '' out* | cut -d':' -f2- | paste -sd+ | bc
you can play in grep with conditions on lines in files
Watch out, this find command will only find stuff in your current directory if there is one file matching outpu*.
One way of doing it:
awk 'END{print NR}' $(find 'outpu*')
Provided that there is not an insane amount of matching filenames that overflows the maximum command length limit of your shell.

Getting a zsh alias including a pipe to execute

I wanted a command that would quickly copy the current tmux window layout to the clipboard on Mac using zsh. I came up with the following:
tmux list-windows | awk '{print $7}' | sed 's/\]$//' | pbcopy
When I run this from the command line it works perfectly with an output like the following:
d97b,135x32,0,0[135x16,0,0{87x16,0,0,0,47x16,88,0,1},135x15,0,17{87x15,0,17,2,47x15,88,17,3}]
However, I can't seem to run it as an alias. If I add the line:
alias layout="tmux list-windows | awk '{print $7}' | sed 's/\]$//' | pbcopy"
to my .zshrc file when I run layout the command does not work as expected. It instead outputs the full tmux list-windows command with the word layout replacing the session name:
0: layout* (4 panes) [135x32] [layout d97b,135x32,0,0[135x16,0,0{87x16,0,0,0,47x16,88,0,1},135x15,0,17{87x15,0,17,2,47x15,88,17,3}]] #0 (active)
What am I doing wrong?
Thanks.
alex_i is correct, if you escape the $7 everything works.
alias layout="tmux list-windows | awk '{print \$7}' | sed 's/\]$//' | pbcopy"
Note the backslash before the $7.
Don't use an alias; use a function:
layout () {
tmux list-windows | awk '{print $7}' | sed 's/\]$//' | pbcopy
}
Then you don't need to worry about quoting.
Is your '$7' interpreted during the .zshrc loading ? Couldn't it be the issue ?

Extract the last directory of a pwd output

How do I extract the last directory of a pwd output? I don't want to use any knowledge of how many levels there are in the directory structure. If I wanted to use that, I could do something like:
> pwd
/home/kiki/dev/my_project
> pwd | cut -d'/' -f5
my_project
But I want to use a command that works regardless of where I am in the directory structure. I assume there is a simple command to do this using awk or sed.
Are you looking for basename or dirname?
Something like
basename "`pwd`"
should be what you want to know.
If you insist on using sed, you could also use
pwd | sed 's#.*/##'
If you want to do it completely within a bash script without running any external binaries, ${PWD##*/} should work.
Using awk:
pwd | awk -F/ '{print $NF}'
Should work for you:
pwd | rev | cut -f1 -d'/' - | rev
Reference:
https://stackoverflow.com/a/31728689/663058

Resources