sed - Separate quotes and arguments - sed

So, I'm trying to get a script I'm working on to run another script in different directories with different arguments as defined in a text file.
Here's part of my code:
for bline in $(cat "$file"); do
lindir=$()
linarg=$()
echo "dir: ${lindir}"
echo "arg: ${linarg}"
done
Let's say I have a line in file that says this:
"./puppies" -c=1 -u=0 -b=1
How can I get an output of ./puppies for lindir and an output of -c=1 -u=0 -b=1 for linarg?

lindir="$( cut -d ' ' -f 1 <<<"$bline" )"
linarg="$( cut -d ' ' -f 2- <<<"$bline" )"
That is
while read -r bline; do
lindir="$( cut -d ' ' -f 1 <<<"$bline" )"
linarg="$( cut -d ' ' -f 2- <<<"$bline" )"
printf "dir: %s\n" "$lindir"
printf "arg: %s\n" "$linarg"
done <"$file"
If you're in a shell that doesn't understand "here-strings":
lindir="$( printf "%s" "$bline" | cut -d ' ' -f 1 )"
linarg="$( printf "%s" "$bline" | cut -d ' ' -f 2- )"

Related

Bash or Python efficient substring matching and filtering

I have a set of filenames in a directory, some of which are likely to have identical substrings but not known in advance. This is a sorting exercise. I want to move the files with the maximum substring ordered letter match together in a subdirectory named with that number of letters and progress to the minimum match until no matches of 2 or more letters remain. Ignore extensions. Case insensitive. Ignore special characters.
Example.
AfricanElephant.jpg
elephant.jpg
grant.png
ant.png
el_gordo.tif
snowbell.png
Starting from maximum length matches to minimum length matches will result in:
./8/AfricanElephant.jpg and ./8/elephant.jpg
./3/grant.png and ./3/ant.png
./2/snowbell.png and ./2/el_gordo.tif
Completely lost on an efficient bash or python way to do what seems a complex sort.
I found some awk code which is almost there:
{
count=0
while ( match($0,/elephant/) ) {
count++
$0=substr($0,RSTART+1)
}
print count
}
where temp.txt contains a list of the files and is invoked as eg
awk -f test_match.awk temp.txt
Drawback is that a) this is hardwired to look for "elephant" as a string (I don't know how to make it take an input string (rather than file) and an input test string to count against, and
b) I really just want to call a bash function to do the sort as specified
If I had this I could wrap some bash script around this core awk to make it work.
function longest_common_substrings () {
shopt -s nocasematch
for file1 in * ; do for file in * ; do \
if [[ -f "$file1" ]]; then
if [[ -f "$file" ]]; then
base1=$(basename "$file" | cut -d. -f1)
base2=$(basename "$file1" | cut -d. -f1)
if [[ "$file" == "$file1" ]]; then
echo -n ""
else
echo -n "$file $file1 " ; $HOME/Scripts/longest_common_substring.sh "$base1" "$base2" | tr -d '\n' | wc -c | awk '{$1=$1;print}' ;
fi
fi
fi
done ;
done | sort -r -k3 | awk '{ print $1, $3 }' > /tmp/filesort_substring.txt
while IFS= read -r line; do \
file_to_move=$(echo "$line" | awk '{ print $1 }') ;
directory_to_move_to=$(echo "$line" | awk '{ print $2 }') ;
if [[ -f "$file_to_move" ]]; then
mkdir -p "$directory_to_move_to"
\gmv -b "$file_to_move" "$directory_to_move_to"
fi
done < /tmp/filesort_substring.txt
shopt -u nocasematch
where $HOME/Scripts/longest_common_substring.sh is
#!/bin/bash
shopt -s nocasematch
if ((${#1}>${#2})); then
long=$1 short=$2
else
long=$2 short=$1
fi
lshort=${#short}
score=0
for ((i=0;i<lshort-score;++i)); do
for ((l=score+1;l<=lshort-i;++l)); do
sub=${short:i:l}
[[ $long != *$sub* ]] && break
subfound=$sub score=$l
done
done
if ((score)); then
echo "$subfound"
fi
shopt -u nocasematch
Kudos to the original solution for computing the match in the script which I found elsewhere in this site

fish shell: Prompt changing to '>'

I recently switched to fish and modified one of the prompts available from fish_config to look like this.
function fish_greeting
fortune
end
function fish_prompt
echo
set -l retc brblack
test $status = 0; and set retc bryellow
set -q __fish_git_prompt_showupstream
or set -g __fish_git_prompt_showupstream auto
function _nim_prompt_wrapper
set retc $argv[1]
set cust $argv[4]
set field_name $argv[2]
set field_value $argv[3]
set_color normal
set_color $retc
echo
echo -n ' ├─'
echo -n '[ '
set_color normal
test -n $field_name
and echo -n $field_name
set_color -o brblack
echo -n ' ▶ '
set_color $retc
set_color $cust
echo -n $field_value
set_color $retc
echo -n ' ]'
end
set_color $retc
echo -n '─┬─'
echo -n '[ '
set_color -o red
echo -n (prompt_hostname)
echo -n ': '
if test "$USER" = root -o "$USER" = toor
set_color -o brred
else
set_color -o brwhite
end
echo -n $USER
set_color -o brblack
echo -n ' ▶ '
set_color -o brcyan
echo -n (pwd)
set_color $retc
echo -n ' ]'
# Virtual Environment
set -q VIRTUAL_ENV_DISABLE_PROMPT
or set -g VIRTUAL_ENV_DISABLE_PROMPT true
set -q VIRTUAL_ENV
and _nim_prompt_wrapper $retc '🐍 ' (basename "$VIRTUAL_ENV") cyan
# git
set prompt_git (fish_git_prompt | string trim -c ' ()')
test -n "$prompt_git"
and _nim_prompt_wrapper $retc (basename -s .git (git config --get remote.origin.url) 2> /dev/null) $prompt_git
# New line
echo
# Background jobs
set_color normal
for job in (jobs)
set_color $retc
echo -n ' │ '
set_color brown
echo $job
end
set_color normal
set_color $retc
echo -n ' ╰─> '
set_color normal
end
The general layout of my prompt:
─┬─[ hostname: user ▶ pwd ]
╰─> _
And blow is what I want instead of >:
─┬─[ hostname: user ]
├─[ pwd ]
╰─> _
OR
─┬─[ hostname: username ]
├─⎡ as_much_as_possible ⎤
├─⎣ the_rest_of_PWD ⎦
╰─> _
But, when $PWD is longer than the window's column size, the whole prompt is just >. I feel that using $COLUMNS should work, but I don't know how I can check the length of pwd before echoing it.
I DO NOT WANT TO USE prompt_pwd.
Thanks in advance! ;)
You can store whatever you want in a variable and then check it, modify it however you want and then echo it.
Here's a rough sketch:
set -l firstline '─┬─[' (prompt_hostname): $USER ▶ $PWD ']'
set -l secondline
if test (string length -- "$firstline") -gt $COLUMNS
# move $PWD to the second line
set firstline '─┬─[' (prompt_hostname): $USER ']'
set secondline '├─[' $PWD ']'
end
echo $firstline
echo $secondline
I DO NOT WANT TO USE prompt_pwd.
You very possibly do. It handles replacing $HOME with ~ (which saves quite a few columns) and does shortening to $fish_prompt_pwd_dir_length characters, or no shortening other than ~ if that's set to 0.
You can even adapt the shortening to $COLUMNS. From my prompt:
# Shorten pwd if prompt is too long
set -l pwd (prompt_pwd)
# 0 means unshortened
for i in $fish_prompt_pwd_dir_length 0 10 9 8 7 6 5 4 3 2 1
set pwd (fish_prompt_pwd_dir_length=$i prompt_pwd)
set -l len (string length -- $prompt_host_nocolor$pwd$last_status$delim' ')
if test $len -lt $COLUMNS
break
end
end

I have a some scritps is working but not work in crontab

I have a some scripts and, when ı was run manually the scripts were run. But
When working in crontab, the format is incorrect.
This is for a new Linux server
awk 'BEGIN{
FS="-"
print "<HTML>""<table border="1" border="3" cellpadding="4" cellspacing="4" bgcolor=lightblue><TH>Firma</TH><TH>Charged Party No</TH><TH>Pcom Status</TH>"
}
{
printf "<TR>"
for(i=1;i<=NF;i++)
printf "<TD>%s</TD>",$i
print "</TR>"
}
END{
print "</TABLE></BODY></HTML>"
}
' /app/ovocontrol/cp_not_found2.txt > file.html
sed -i "s/failure/<font color="red">failure<\/font>/g;s/success/<font color="green">success<\/font>/g" file.html
(
echo "To: **********"
echo "Subject: Son 10 Dakikaya ait Toplu SMS CUDB Hata Detayi"
echo "Content-Type: text/html"
echo
cat file.html
echo
) | /usr/sbin/sendmail -t
I have a some scripts and, when ı was run manually the scripts were run. But
When working in crontab, the format is incorrect.
You don't have a shebang, nor do you have a complete crontab, so I'm guessing at what you're actually doing. I suspect you are trying to call those multiple commands directly from your crontab, which is a terrible idea. Instead, put your multiple calls into a single script and invoke it from cron. eg, do something like:
$ cat > /path/to/script << 'EOF'
#!/bin/sh
: ${f:=/app/ovocontrol/cp_not_found2.txt}
{
echo "To: **********"
echo "Subject: Son 10 Dakikaya ait Toplu SMS CUDB Hata Detayi"
echo "Content-Type: text/html"
echo
printf '<HTML><table border="1" border="3" cellpadding="4" cellspacing="4"'
printf ' bgcolor=lightblue><TH>Firma</TH><TH>Charged Party No</TH><TH>Pcom Status</TH>\n'
awk -F - ' {
printf "<TR>"
for(i=1;i<=NF;i++) printf "<TD>%s</TD>",$i
print "</TR>"
}
' "$f"
printf '</TABLE></BODY></HTML>\n'
} \
| sed -e 's#failure#<font color="red">failure</font>#g' \
-e 's#success#<font color="green">success</font>#g'
| /usr/sbin/sendmail -t
EOF
$ chmod +x /path/to/script
$ printf 'i\n0 * * * * /path/to/script\n.\nw\nq\n' | EDITOR=ed crontab -e
Note that the last command above is not really a great idea, just an attempt to codify the directive to add /path/to/script to your crontab.
I solved problem other way , "awk '!seen[$0]++'" command incorrect my format. the code was actually a short portion of the code. I think crontab has a special settings.

executing a bash script in perl

I want to run this command in perl
for dir in *; do
test -d "$dir" && ( find "$dir" -name '*test' | grep -q . || echo "$dir" );
done
I have tried :
system ("for dir in *; do
test -d "\$dir" && ( find "\$dir" -name '*test' | grep -q . || echo "\$dir" );
done");
but does not work .
A pure Perl implementation using File::Find module's find function:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
find \&find_directories, '.';
sub find_directories {
if ( -d && $File::Find::name =~ /test$/ ) {
print "$File::Find::name\n";
}
}
Your quoting is off.
"for dir in *; do
test -d "\$dir" && ( find "\$dir" -name '*test' | grep -q . || echo "\$dir" );
done"
You have decided to delimit your string with double quotes ", but they are included in your string.
Either escape the other quotes:
"for dir in *; do
test -d \"\$dir\" && ( find \"\$dir\" -name '*test' | grep -q . || echo \"\$dir\" );
done"
(error prone, ugly)
… or use another delimiter: Perl offers you a wide range of possibilities. These quoting syntaxes interpolate variables inside: "…" and qq{…} where you can use any character in [^\s\w] as delimiter, and non-interpolating syntaxes are: '…' and q{…} with the same delimiter flexibility as before:
qq{for dir in *; do
test -d "\$dir" && ( find "\$dir" -name '*test' | grep -q . || echo "\$dir" );
done}
The q and qq constructs can include the delimiter inside the string, if the occurrence is balanced: q( a ( b ) c ) works.
The third quoting mechanism is a here-doc:
system( <<END_OF_BASH_SCRIPT );
for dir in *; do
test -d "\$dir" && ( find "\$dir" -name '*test' | grep -q . || echo "\$dir" );
done
END_OF_BASH_SCRIPT
This is usefull for including longer fragments without worrying about a delimitor. The String is ended by a predefined token that has to appear on a line of its own. If the delimitor declaration is placed in single quotes (<<'END_OF_SCRIPT'), no variables will be interpolated:
system( <<'END_OF_BASH_SCRIPT' );
for dir in *; do
test -d "$dir" && ( find "$dir" -name '*test' | grep -q . || echo "$dir" );
done
END_OF_BASH_SCRIPT
Note on the q{} and qq{} syntax: This is a feature never to be used outside of obfuscation, but it is possible to use a character in \w as the delimiter. You have to include a space between the quoting operator q or qq and the delimiter. This works: q xabcx and is equal to 'abc'.
Instead of starting the script, try starting a bash instance that runs the script. E.g.
system("bash -c 'for dir bla bla bla'");
system() uses your default system shell, which is probably not Bash. The solution is to call Bash explicitly with the system() command.

get list of sections from ini-file using shell (sed/awk)

I want to create a var from the section names of an ini file like:
[foo]
; ...
[bar]
; ...
[baz:bar]
;...
now I need a var like
SECTIONS="foo bar baz"
thanks in advance
One line solution could be:
export SECTIONS=`grep "^\[" test.ini |sort -u | xargs | tr '\[' ' ' | tr '\]' ' ' `
SECTIONS=$(crudini --get your.ini | sed 's/:.*//')
I'm now using this construct, don't need to know if a section exists. just read it, if it's empty it does not exist.
INI_FILE=test.ini
function ini_get
{
eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
-e 's/;.*$//' \
-e 's/[[:space:]]*$//' \
-e 's/^[[:space:]]*//' \
-e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
< $INI_FILE \
| sed -n -e "/^\[$1\]/,/^\s*\[/{/^[^;].*\=.*/p;}"
echo ${!2}
}
IP=$(ini_get 50001 ip)
PORT=$(ini_get 50001 port)
echo $IP:$PORT