bash: how do I concatenate the output of two commands so that I can pipe them to a third? - bash

$ hg status
and
$ hg status --ignored
give very similar outputs. I'd like to concatenate them so I can feed them to awk, as if there were an hg status --all (or svn's svn status --no-ignore)
I'm thinking something like:
$ echo "$(hg status)" "$(hg status --ignored)" | awk ' ( $1 == "?" ) || ( $1 == "I") { print $2 }' | xargs rm -r
to make a 'make very clean indeed' command, but it seems to occasionally leave a file behind, perhaps because a newline goes missing or something.

Use curly braces to group commands:
$ { echo first line; echo second line; } | grep "line"
first line
second line
(Posted as an answer from camh's comment)

You can use a subshell:
( hg status; hg status --ignored ) | awk '( $1 == "?" ) || ( $1 == "I") { print $2 }' | xargs rm -r

You can use the rest of the hg status flags to show what you really want:
hg status -uriamn
That shows unknown files (u), removed files (r), ignored (i), added (a), modified (m) and does so without showing the status prefix.

This works for me:
echo $(a)$(b)
if you add "" you can add delimiters eg.:
echo "$(./gethostname.sh)|($(./getip.sh);"
I use this on Openwrt to broadcast my ip settings:
echo "$( uci get system.#system[0].hostname )|$( ip addr | grep inet | grep br-lan | cut -d ' ' -f 6 | cut -d '/' -f 1 );" | socat - UDP-DATAGRAM:255.255.255.255:4999,broadcast ;

Related

Bash Code with while, list and crontab command

I have something like this :
all_scripts=()
for i in "${OS_USER[#]}"; do
list_script=$(crontab -u $i -l | grep -o '[^ ]*\.sh' | grep -o '^[^ ]*' | sort -u)
I want to create a list ( all_scripts) which contain all scripts from all users
For example
if in user 1 crontab there are script1.sh , script2.sh
in user 2 there are scrip3.sh, script4.sh, script5.sh
and in user 3 script6.sh
I want a list with all scripts : script1,sh, script2.sh, script3,...script6.sh
What you presented is incomplete. What you have is almost all correct.
You need to include the action ... to build the list ... within the loop.
You also need to initialize list_script as an array.
You probably want to suppress the "no crontab for ..." messaging.
You also need to be selective regarding which fields are "harvested" from the crontab, so ... ignore comments and ignore parameter definitions.
I recognize that the below may not correctly handle parameter definitions that may include spaces, but it is a good start for what you are looking for. So, the end result would look something like this:
#!/bin/bash
## You may have a specific list instead of this next line.
OS_USER=( $(cut -f1 -d\: /etc/passwd) )
all_scripts=()
list_script=()
for i in ${OS_USER[#]}
do
list_script=$(crontab -u $i -l | grep -v '^#' | awk '{
pos=index($0, $6) ;
$0=substr($0, pos) ;
print NF ;
for( i=1 ; i<=NF ; i++){
if( index($i, "=") == 0 ){
print $i ;
break ;
} ;
} ;
}' | grep -o '[^ ]*\.sh' | grep -o '^[^ ]*' | sort -u )
all_scripts=( ${allscripts[#]} ${list_script[#]} )
done
print ${allscripts[#]}

Numbered options for bash completion

Can we get numbered completion options with bash when we have started typing part of the option? For example with ksh I am able to get a numbered list of options and then when I press 3 followed by 3 I get the 3rd option completed.
> cat local.
1) local.cshrc
2) local.login
3) local.profile
> cat local.profile
Is this kind of completion possible using bash programmable completion?
With something like this I am able to give numbers for all available options, but this breaks when the user starts to type the option and presses tab so a fallback to normal options without numbers has to be used.
(( COMP_CWORD > 2 )) && return #This stops completion at third word for z oe
if [[ "${COMP_WORDS[2]}" = "" || $( echo "${COMP_WORDS[2]}" | perl -ne 'use Scalar::Util qw(looks_like_number); if (looks_like_number($_)){print 1}else{print 0}' ) -eq 1 ]]; then
#Show all instances oratab + running
local suggestions=($( compgen -W "$( { z ot | grep -v ^$ | grep -v SIDs | grep -v : && z p1; } | perl -lpe 's/\s+$//' | sort | uniq | perl -ne 'print "$.) $_"' )" -- "${COMP_WORDS[2]}" ))
if [ "${#suggestions[#]}" == "1" ]; then #One match found
local cmd=$( echo "${suggestions[0]}" | perl -pe 's/[1-9]+\)[\s]+//' )
COMPREPLY=("$cmd ") #Space needed here to handle -o nospace
return
else
#More than one suggestions resolved, respond with the suggestions intact
COMPREPLY=("${suggestions[#]}")
return
fi
else
local suggestions=( $( compgen -W "$( { z ot | grep -v ^$ | grep -v SIDs | grep -v : && z p1; } | perl -lpe 's/\s+$//' | sort | uniq )" -- "${COMP_WORDS[2]}" ) )
if [ "${#suggestions[#]}" == "1" ]; then #One match found
local cmd="${suggestions[0]}"
COMPREPLY=("$cmd ") #Space needed here to handle -o nospace
return
else
#More than one suggestions resolved, respond with the suggestions intact
COMPREPLY=("${suggestions[#]}")
return
fi
fi
Is there way to do the ksh like completion number + tab with bash completion?

adding history reverse line number to a bash script

I have the following nice little bash function to make searches in my history (here for example, looking for ls commands):
history | grep --color=always ls | sort -k2 | uniq -f 1 | sort -n
I packaged it into a bash script, linked to an alias (histg) and it works great:
#!/bin/bash
if [ "$1" == "-h" ]; then
echo "A bash script to find patterns in history, avoiding duplicates (also non consecutive)"
echo "expands to: XX"
exit 0
fi
HISTSIZE=100000 # need this, because does not read .bashrc so does not know how big a HISTSIZE
HISTFILE=~/.bash_history # Or wherever you bash history file lives
set -o history # enable history
OUTPUT="$(history | grep --color=always $1 | sort -k2 | uniq -f 1 | sort -n)"
echo "${OUTPUT}"
Typically, I get this kind of output:
$ histg SI
16424 git commit -m "working on SI"
16671 git commit -m "updated SI"
17782 cd SI/
However I want to do one more improvement, and I do not know how to proceed. I want to be able to quickly call those commands again, but as you see I have a big hist, so typing !17782 is a bit long. If the current size of my history is for example 17785 (I have a max history size 100000), I would like to see:
$ histg SI
16424 -1361 git commit -m "working on SI"
16671 -1114 git commit -m "updated SI"
17782 -3 cd ~/Desktop/crrt/wrk/SI/
so that I can type in -3
Any idea how I can adapt my bash command to add this column?
In a first try, my code was not working as expected because the negative numbers didn't match: the current session history was not taken into account. So I changed your script to a function (to add to .bashrc). The tricky part is handled by awk:
function histg() {
history | grep --color=always $1 | sort -k2 | uniq -f 1 | sort -n \
| awk '
BEGIN { hist_size = '$(history|wc -l)' }
{
n = $1; $1 = ""
printf("%-7i %-7i %s\n", n, n - hist_size, $0)
}'
history -d $(history 1)
}
The last line deletes the call to histg in history, so the negative numbers still keep sense.

foreach: grep backtick in for-loop

How would one grep backtick from files in a for-loop.
I would like to run grep for a pattern '`define'. The pattern works in standalone grep command but fails in for-loop.
foreach xxx ( `grep -r '`define' $idirectory --no-filename | sed -e 's ; //.* ; ; ' -e 's ; #.* ; ; ' -e 's ; ^\s* ; ; ' | grep -v ^$ | sort -n | awk '{print $2}' | uniq -d`)
echo $xxx
end
The backticks are conflicting in the for-loop.
regards
Srisurya
Simply, don't use ' and escape the backtick with backshlash.
So, the next didn't works:
grep -r '`def' *
and prints
No matching command
But this:
grep -r \`def *
works and prints
ewdwedwe `define`
So, simiarly for your script, the next works (file btick.tcsh):
#!/bin/tcsh
set greparg = \`def
foreach xxx ( `grep -l $greparg *` )
echo ===$xxx===
end
and pruduces the next result
===btick.tcsh===
===btick1.txt===
===btick2.txt===
the content of btick.txt files:
btick1 `def`
This is an alternate solution.
Use of ASCII code for grep argument
grep -rP '\x60define' $idirectory
where \x60 is the ascii code for "`"
You should not use old and outdated back ticks, use parentheses like this $(code)
Try this:
for xxx in $(some code $(som more code)
echo "$xxx"
done
Nesting and back tics makes it complicated, it need to be escaped. Compare this to:
listing=`ls -l \`cat filenames.txt\``
vs
listing=$(ls -l $(cat filenames.txt))

UNIX shell: how do you tail up to a searchable expression?

The end of git status looks like this:
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# Classes/Default.png
# Classes/Default#2x.png
...
Since you might have any number of untracked files, I'm trying to tail from the end of the file to "Untracked files" and save it to a temp file, strip out the first three lines and convert the filenames to git add Classes/...
I can't seem to find a good way (other than maybe a different language) to tail up to a searchable expression. Thanks!
Use sed to print everything from "Untracked files" to the end:
git status | sed -n '/Untracked files:$/,$p'
Then you just have to parse the filenames by removing the # character.
You can also use git status -s to get a shorter, more easily parsed output:
~$ git status -s
?? Classes/Default.png
?? Classes/Default#2x.png
This is a good application of awk, which lets you grep and extract at the same time:
~$ git status -s | awk '/\?\?/{print $2}'
Classes/Default.png
Classes/Default#2x.png
Alternatively: awk '{if ($1 == "??") print $2}'
You can also, of course, use git add to list (and add) untracked files.
Use the tail command:
tail -$(($(wc -l < file.txt) - $(grep -n "Untracked files" file.txt|cut -d: -f1) - 2)) file.txt
How it works:
total number of lines in file = wc -l < file.txt
line number of "Untracked files" = grep -n "Untracked files" file.txt|cut -d: -f1
The -2 is to remove the top lines
Complete command with git add:
tail -$(($(wc -l < file.txt) - $(grep -n "Untracked files" file.txt|cut -d: -f1) - 2)) file.txt | tr -d '#'| while read line; do echo git add $line; done
Pipe it to:
perl -e 'my ($o, $p) = (0, shift); while(<>){print if $o or $o = /$p/}' "$MY_REGEX"
Where $MY_REGEX is your pattern. In your case, probably '^\s{7}'.
Solution using shell scripting.
First start reading the file in a while loop, keep a count of the number of lines read, break the loop when the required line is found.
Using the count of lines tail the file and then extract file names using awk.
i=0;
l=`wc -l filename | awk '{print $1}'`;
while read line;
do i=`echo $i + 1 | bc`;
if [[ $line == "# Untracked files:" ]];
then break;
fi;
done < "filename";
tail -`echo $l -$i -2 | bc` filename | awk -F "/" '{print $NF}'
Here "filename" is the file you want to process

Resources