Head command deletes part of line [duplicate] - bash

This question already has answers here:
I just assigned a variable, but echo $variable shows something else
(7 answers)
Closed 2 years ago.
I have a csv file (settings.csv) with the following data (separator is a space):
-n $(output_name)
--outdir “peaks/$(output_name)”
-g hs
-f AUTO
--qvalue 0.05
--extsize 200
-B
sample input output_name
sample-chip1 sample-input1 sample1
sample-chip2 sample-input2 sample2
When I call head -7 settings.csv I get this expected output:
-n $(output_name)
--outdir “peaks/$(output_name)”
-g hs
-f AUTO
--qvalue 0.05
--extsize 200
-B
Next I tried to store this output in a variable called settings with settings=$(head -7 settings.csv) and when I echo $settings I get this output:
$(output_name) --outdir “peaks/$(output_name)” -g hs -f AUTO --qvalue 0.05 --extsize 200 -B
It has deleted the -n part of the first line in the settings.csv, and I don't know why. How can I create $settings without losing -n?

The settings variable already contains the -n option you expect to be there. It’s just that the echo command interprets it as its own (echo’s) option.
One way to avoid this problem is to quote the argument:
echo "$settings"
Alternatively, you can use
printf '%s\n' "$settings"

Related

Delete duplicate commands of zsh_history keeping last occurence

I'm trying to write a shell script that deletes duplicate commands from my zsh_history file. Having no real shell script experience and given my C background I wrote this monstrosity that seems to work (only on Mac though), but takes a couple of lifetimes to end:
#!/bin/sh
history=./.zsh_history
currentLines=$(grep -c '^' $history)
wordToBeSearched=""
currentWord=""
contrastor=0
searchdex=""
echo "Currently handling a grand total of: $currentLines lines. Please stand by..."
while (( $currentLines - $contrastor > 0 ))
do
searchdex=1
wordToBeSearched=$(awk "NR==$currentLines - $contrastor" $history | cut -d ";" -f 2)
echo "$wordToBeSearched A BUSCAR"
while (( $currentLines - $contrastor - $searchdex > 0 ))
do
currentWord=$(awk "NR==$currentLines - $contrastor - $searchdex" $history | cut -d ";" -f 2)
echo $currentWord
if test "$currentWord" == "$wordToBeSearched"
then
sed -i .bak "$((currentLines - $contrastor - $searchdex)) d" $history
currentLines=$(grep -c '^' $history)
echo "Line deleted. New number of lines: $currentLines"
let "searchdex--"
fi
let "searchdex++"
done
let "contrastor++"
done
^THIS IS HORRIBLE CODE NOONE SHOULD USE^
I'm now looking for a less life-consuming approach using more shell-like conventions, mainly sed at this point. Thing is, zsh_history stores commands in a very specific way:
: 1652789298:0;man sed
Where the command itself is always preceded by ":0;".
I'd like to find a way to delete duplicate commands while keeping the last occurrence of each command intact and in order.
Currently I'm at a point where I have a functional line that will delete strange lines that find their way into the file (newlines and such):
#sed -i '/^:/!d' $history
But that's about it. Not really sure how get the expression to look for into a sed without falling back into everlasting whiles or how to delete the duplicates while keeping the last-occurring command.
The zsh option hist_ignore_all_dups should do what you want. Just add setopt hist_ignore_all_dups to your zshrc.
I wanted something similar, but I dont care about preserving the last one as you mentioned. This is just finding duplicates and removing them.
I used this command and then removed my .zsh_history and replacing it with the .zhistory that this command outputs
So from your home folder:
cat -n .zsh_history | sort -t ';' -uk2 | sort -nk1 | cut -f2- > .zhistory
This effectively will give you the file .zhistory containing the changed list, in my case it went from 9000 lines to 3000, you can check it with wc -l .zhistory to count the number of lines it has.
Please double check and make a backup of your zsh history before doing anything with it.
The sort command might be able to be modified to sort it by numerical value and somehow archieve what you want, but you will have to investigate further about that.
I found the script here, along with some commands to avoid saving duplicates in the future
I didn't want to rename the history file.
# dedupe_lines.zsh
if [ $# -eq 0 ]; then
echo "Error: No file specified" >&2
exit 1
fi
if [ ! -f $1 ]; then
echo "Error: File not found" >&2
exit 1
fi
sort $1 | uniq >temp.txt
mv temp.txt $1
Add dedupe_lines.zsh to your home directory, then make it executable.
chmod +x dedupe_lines.zsh
Run it.
./dedupe_lines.zsh .zsh_history

How to parse out specific filenames from basename in bash

I'm working on the following script for a research project with my school
for f in $(ls Illumina_Data/Aphyllon/PE150_2016_04_05* ); do
if [[ "${f}" == *"_R1"* ]] ;then
echo "INITIALIZE THE SEQUENCE"
echo `basename " ${f%%_R1*}"`
get_organelle_from_reads.py -1 ${f%%_R1*}_R1_001.fastq.gz \
-2 ${f%%_R1*}_R2_001.fastq.gz \
-o Sequenced_Aphyllon_Data/`basename "${f%%_R1*}"` \
-R 15 -k 21,45,65,85,105 -F embplant_pt
fi
done
What we're getting with this script right now is kinda of a long name and we're wanting it to be shorter for organization sake. If you take a look at the -o command and the section that says Sequenced_Aphyllon_Data/'basename "${f%%_R1*}"'. What this is spitting out is the entire fastq file name that we originally used of the following format
A_speciesname_IDtag_(some set of number and letters)_(some set of numbers and letters)_(some set of number and letters)_(some set of numbers and letters)
The issue I'm having is that we're wanting the A_speciesname_IDtag section to remain, though sometimes our reads don't contain the IDtag section which makes it so we need to parse at either the second or third _ from the left. However there are always four _ from the right without fail.
So is there a way to specifically target an _ from the right of a string? From the right the amount of _ separating what we need will always remain the same but will change from the left.
grep with a lookahead assertion?
$ s1=dog_ID1_a000_b111_c222_d333
$ s2=cat_a000_b111_c222_d333
$ grep -oP ".+(?=_\w+_\w+_\w+_\w+)" <<<$s1
dog_ID1
$ grep -oP ".+(?=_\w+_\w+_\w+_\w+)" <<<$s2
cat

Cannot echo bash variable from single line command [duplicate]

This question already has answers here:
Rename multiple files, but only rename part of the filename in Bash
(6 answers)
Closed 4 years ago.
I have the following file names where I am trying to relabel v5.4b to v5.7:
v5.4b_lvl-1.e8974326
v5.4b_lvl-1.o8974326
v5.4b_lvl-1.pe8974326
v5.4b_lvl-1.po8974326
v5.4b_lvl-2.1.e8974303
v5.4b_lvl-2.1.o8974303
v5.4b_lvl-2.1.pe8974303
v5.4b_lvl-2.1.po8974303
v5.4b_lvl-2.2.e8974304
v5.4b_lvl-2.2.o8974304
v5.4b_lvl-2.2.pe8974304
v5.4b_lvl-2.2.po8974304
v5.4b_lvl-3.1.e8974305
v5.4b_lvl-3.1.o8974305
v5.4b_lvl-3.1.pe8974305
v5.4b_lvl-3.1.po8974305
v5.4b_lvl-4.1.e8974327
v5.4b_lvl-4.1.o8974327
v5.4b_lvl-4.1.pe8974327
v5.4b_lvl-4.1.po8974327
I can't do mv v5.4b_* v5.7_* because it thinks v5.7_* is a directory so I am trying a for-loop but I can't get it to work
I am trying the recommended answer from this SO post How to set a variable to the output of a command in Bash? but getting a bunch of empty lines.
What am I doing incorrectly? How can I save the output of cut to SUFFIX so I can mv $i v5.7_$SUFFIX?
-bash-4.1$ for i in v5.4b*; do echo $i | SUFFIX=`cut -f2 -d'_'`; echo ${SUFFIX}; done
You've got echo $i in the wrong place. The output of that command needs to be piped to cut for it to read anything, then the result is assigned to SUFFIX:
for i in v5.4b*
do
SUFFIX=`echo $i | cut -f2 -d'_'`
echo ${SUFFIX}
done
If you rename utility then just do:
rename -n 's/v5\4.b/v5.7/' v5.4b*
PS: -n is for dry-run. You may remove it later for real renaming.
If rename is not available then use:
for i in v5.4b*; do
echo mv "$i" "${i/v5.4b/v5.7}"
done
Remove 'echo` if you're satisfied with the output.

Would a "shell function" or "alias" be appropriate for this use

I'm currently trying to create an alias or shell function which I can run to check my battery life, in attempts to familiarize myself with aliases and bash. I have run into a problem where, I'm not receiving any feedback from my command and can not verify if it's working or if there are any steps i have left out that will give me my desired result.
Current .bashrc alias:
alias battery='upower -i $(upower -e | grep -e 'BAT'| grep -E "state|to\ full|percentage")'
Desired use:
b#localhost:~$ battery
Desired result:
state: discharging Time to empty: x.x Hours percentage: xx%
I have read the bash references for something that might help me here. I wasn't able to find anything that I think applies here. Thanks for your consideration!
As #bannji already announced in a comment, he has fixed his command.
Old incorrect alias
'upower -i $(upower -e | grep -e 'BAT'| grep -E "state|to\ full|percentage")'
New correct alias
'upower -i $(upower -e | grep -e "BAT") | grep -E "state|to\ full|percentage"'
Most comments were talking about the interpretation of the quotes. That was not the problem here. The main difference is where the subcommand is closed. In the first case the subcommand is closed after the last grep, su upower -i gets nothing.
In the second command the second grep will filter the output of upower -i.
The difference in quotes is interesting in an other example.
addone() {
((sum=$1+1))
echo "${sum}"
}
i=1
alias battery='addone $(addone $i)'
i=4
battery
# other alias
i=1
alias battery2='addone $(addone '$i')'
i=4
battery2
Both battery commands will try to add 2 to the value of $i, but will give different results.
The command battery will add 2 to the current value 4 of $i, resulting in 6.
The command battery2 will add 2 to the value of $i at the moment that the alias was defined, resulting in 3.
Why?
In battery2 the string $i is surrounded by single quotes, but those single quotes are inside other ones. The result is that $i is evaluated and the alias is defined as
alias battery2='addone $(addone 2)'

How can I build a dynamic parameter list for mailx command in bash linux? [duplicate]

This question already has answers here:
build argument lists containing whitespace
(5 answers)
Closed 6 years ago.
OS: Red Hat Enterprise Linux Server release 5.11 (Tikanga)
I have a code sniplet:
!/usr/bin/env bash
v_mailx_parameter=""
v_cfg_email_adresse_to="john.doe_to#gmail.com"
v_cfg_email_subject="Report from December 2016"
v_tmp_email_text_name="Message Body"
v_email_main_file="appel orange.txt" # -> There is a SPACE in the file name!
v_tmp_path="/home/server/tmp/"
if [ ! -z "${v_email_main_file}" ]
then v_mailx_parameter="${v_mailx_parameter} -a \"${v_tmp_path}${v_email_main_file}\""
/\
||
Here is the problem but I need this because of spaces in the name
fi
echo -e "/bin/mailx ${v_mailx_parameter} -s \"${v_cfg_email_subject}\" \"${v_cfg_email_adresse_to}\""
cat ${v_tmp_email_text_name} | /bin/mailx ${v_mailx_parameter} -s "${v_cfg_email_subject}" "${v_cfg_email_adresse_to}"
exit
PROBLEM:
I want to build up the parameters to the mailx command dynamically.
But I want to use the " double-quotes because there can be spaces in the file names.
Unfortunately the example above does not work... because the mailx takes the double-quotes as if they belong to the file name...and I get an error message.
I do not want to use the if else condition.. because I have many of files I need to send... I need a dynamic solution.
Maybe it is better to understand what my problem is:
It works in such a hardcoded way:
cat ${v_tmp_email_text_name} | /bin/mailx -a "/home/server/tmp/appel orange.txt" -s "${v_cfg_email_subject}" "${v_cfg_email_adresse_to}"
But so NOT:
v_mailx_parameter="-a \"${v_tmp_path}${v_email_main_file}\""
cat ${v_tmp_email_text_name} | /bin/mailx ${v_mailx_parameter} -s "${v_cfg_email_subject}" "${v_cfg_email_adresse_to}"
Thanks!
You need to use an array, which can hold all the arguments to easy logging and invocation. Note, though, that the input redirection to feed the message to mailx is not an argument.
#!/usr/bin/env bash
v_cfg_email_adresse_to="john.doe_to#gmail.com"
v_cfg_email_subject="Report from December 2016"
v_tmp_email_text_name="Message Body"
v_email_main_file="appel orange.txt"
v_tmp_path="/home/server/tmp/"
if [ ! -z "${v_email_main_file}" ]; then
v_mailx_parameters+=( -a "${v_tmp_path}${v_email_main_file}" )
fi
v_mail_x_parameters+=( -s "${v_cfg_email_subject}" )
v_mail_x_parameters+=( "${v_cfg_email_adresse_to}" )
printf '/binmailx %s < %s\n' "${v_mail_x_parameters[*]}" "${v_tmp_email_text_name}"
/bin/mailx "${v_mailx_parameters[#]}" < "${v_tmp_email_text_name}"
One easy way is to use an array.
declare -a parameters=()
parameters+=(-option1 OPTION VALUE)
parameters+=(-option2 "VALUE WITH SPACES")
parameters+=("/path/with spaces")
parameters+=("$PATH_WITH_SPACES")
if
some_condition
then
parameters+=(-optionA)
else
parameters+=(-optionB)
fi
mailx "${parameters[#]}"
The "${parameters[#]}" expansion is special in that it ensure each of the element is separately quoted, without performing word splitting or expansion on the actual value of the elements (like when using "$#").

Resources