How can I have a newline in a string in fish shell? - string

Is same question as
How can I have a newline in a string in sh?
but in fish shell.
In bash we have:
$'this is line\nthis is another line'
who produces the expected result, but in fish this doesnt works.
fish has a similar way to do this?
Edit 1:
has the literal method wisely mentioned by the faho:
'this is line
this is another line'
But i'm really curious about the existence of a method keeping reference for \n as line break like in shell.
I want to know if I can use this string "this is a line\nthis is another line" making sure that the fish will consider \n as a line break and not a literal.

Fish replaces \n outside of quotes with a newline, so you can stop and restart the quotes (note no "$" before the quotes):
'this is line'\n'this is another line'
Fish follows quotes across lines, so you can include a literal newline:
'this is line
this is another line'

You can use echo -e (Enable interpretation of backslash escapes), as pointed out by #faho in his comment:
$ echo -e "## Foo\nBar"
## Foo
Bar
From man echo:
Escape sequences
If -e is used, the following sequences are recognized:
o \ backslash
o \a alert (BEL)
o \b backspace
o \c produce no further output
o \e escape
o \f form feed
o \n new line
o \r carriage return
o \t horizontal tab
o \v vertical tab
o \0NNN byte with octal value NNN (1 to 3 digits)
o \xHH byte with hexadecimal value HH (1 to 2 digits)

Edit your config.fish file.
Example in Ubuntu sudo nano /etc/fish/config.fish
Prompt function is what controls the appearance of the fish terminal. Paste this code:
# Put system-wide fish configuration entries here
# or in .fish files in conf.d/
# Files in conf.d can be overridden by the user
# by files with the same name in $XDG_CONFIG_HOME/fish/conf.d
# This file is run by all fish instances.
# To include configuration only for login shells, use
# if status --is-login
# ...
# end
# To include configuration only for interactive shells, use
# if status --is-interactive
# ...
# end
#
function fish_prompt -d "Write out the prompt"
# change output color
set -U fish_color_command '2cff8f' -o
# Aliases
alias cls='clear'
# Prompt
printf '%s#%s%s%s%s\n#>' (whoami) (hostname | cut -d . -f 1) \
(set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
end

Related

Bash: Replace word with spaces equal to the length of the word

I thought my bash-fu was strong enough but apparently it isn't. I can't seem to figure this out. I would like to do something like this:
var="XXXX This is a line"
word_to_replace="XXXX"
# ...do something
echo "Done:${var}"
Done: This is a line
Basically I want to quickly replace all characters in a word with spaces, preferably in one step. Note, if it makes things easier var currently will be at the start of the string although it may have leading spaces (which would need to be retained).
In python I would possibly do this:
>>> var="XXXX This is a line"
>>> word_to_replace="XXXX"
>>> var=var.replace(word_to_replace, ' '*len(word_to_replace))
>>> print("Done:%s" % var)
Done: This is a line
Here's one way you could do it, using a combination of shell parameter expansion and the sed command.
$ var="XXXX This is a line"
$ word_to_replace="XXXX"
$ replacement=${word_to_replace//?/ }
$ sed "s/$word_to_replace/$replacement/" <<<"$var"
This is a line
? matches any character and ${var//find/replace} does a global substitution, so the variable $replacement has the same length as $word_to_replace, but is composed solely of spaces.
You can save the result to a variable in the usual way:
new_var=$(sed "s/$word_to_replace/$replacement/" <<<"$var")
In plain Bash:
If we know the word to be replaced:
$ line=" foo and some"
$ word=foo
$ spaces=$(printf "%*s" ${#word} "")
$ echo "${line/$word/$spaces}"
and some
If we don't, we could pick the string apart to find the leading word, but this gets a bit ugly:
xxx() {
shopt -s extglob # for *( )
local line=$1
local indent=${line%%[^ ]*} # the leading spaces
line=${line##*( )} # remove the leading spaces
local tail=${line#* } # part after first space
local head=${line%% *} # part before first space...
echo "$indent${head//?/ } $tail" # replace and put back together
}
$ xxx " word on a line"
on a line
That also fails if there is only one word on the line, head and tail both get set to that word, we'd need to check for if there is a space and handle the two cases separately.
Using sed:
#!/usr/bin/env sh
word_to_replace="XXXX"
var="$word_to_replace This is a line"
echo "Done: $var"
word_to_replace=$(echo "$word_to_replace" | sed 's,., ,g')
var="$word_to_replace This is a line"
echo "Done: $var"
I use GNU Awk:
echo "$title" | gawk '{gsub(/./, "*"); print}'
This replaces each character with an asterisk.
EDIT. Consolidated answer:
$ export text="FOO hello"
$ export sub="FOO"
$ export space=${sub//?/ }
$ echo "${text//$sub/$space}"
hello

How to use sed to replace a command followed by 0 or more spaces in bash

I can't figure out how to replace a comma followed by 0 or more spaces in a bash variable. here's what i have:
base="test00 test01 test02 test03"
options="test04,test05, test06"
for b in $(echo $options | sed "s/, \+/ /g")
do
base="${base} $b"
done
What i'm trying to do is append the "options" to the "base". Options is user input which can be empty or a csv list however that list can be
"test04, test05, test06" -> space after the comma
"test04,test05,test06" -> no spaces
"test04,test05, test06" -> mixture
what i need is my output "base" to be a space delimited list however no matter what i try my list keeps getting cut off after the first word.
My expected out is
"test00 test01 test02 test03 test04 test05 test06"
If your goal is to generate a command, this technique is wrong altogether: As described in BashFAQ #50, command arguments should be stored in an array, not a whitespace-delimited string.
base=( test00 test01 test02 test03 )
IFS=', ' read -r -a options_array <<<"$options"
# ...and, to execute the result:
"${base[#]}" "${options_array[#]}"
That said, even this isn't adequate to many legitimate use cases: Consider what happens if you want to pass an option that contains literal whitespace -- for instance, running ./your-base-command "base argument with spaces" "second base argument" "option with spaces" "option with spaces" "second option with spaces". For that, you need something like the following:
base=( ./your-base-command "base argument with spaces" "second base argument" )
options="option with spaces, second option with spaces"
# read options into an array, splitting on commas
IFS=, read -r -a options_array <<<"$options"
# trim leading and trailing spaces from array elements
options_array=( "${options_array[#]% }" )
options_array=( "${options_array[#]# }" )
# ...and, to execute the result:
"${base[#]}" "${options_array[#]}"
No need for sed, bash has built in pattern substitution parameter expansion. With bash 3.0 or later, extglob added support for more advanced regular expressions.
# Enables extended regular expressions for +(pattern)
shopt -s extglob
# Replaces all comma-space runs with just a single space
options="${options//,+( )/ }"
If you don't have bash 3.0+ available or don't like enabling extglob, simply strip all spaces which will work most of the time:
# Remove all spaces
options="${options// /}"
# Then replace commas with spaces
options="${options//,/ }"

Remove a sequence of chars at the end of file including LF (linefeed)

I have a file that contains some PCL sequences. I have this sequence at the end of the file (hex):
461b 2670 3158 0a F.&p1X.
I want to remove the sequence: <Esc>&p1X including the character that follows. In 99% of cases, LF follows the sequence.
I tried this command:
sed -b 's/\o33&p[0-9]X$//Mg' ~/test.txt >test2.txt
However, it appends LF at the end of test2.txt. Also, if, instead of $ I specify . it doesn't match the line anymore.
If you want to play with this, generate the input file using this command:
echo -e "SomeString\033&p1X" > ~/test.txt
The redirect appends an LF char at the end.
Thanks
If I have understood well you know for sure that your file contains that sequence of characters at the end. If this is the case I would simply truncate the last six bytes. It will work regardless the very last character being new-line or whatever you want...
Example:
$ echo -e "SomeString\033&p1X" > test.txt
$ od -c test.txt
0000000 S o m e S t r i n g 033 & p 1 X \n
0000020
$ truncate -s -6 test.txt
$ od -c test.txt
0000000 S o m e S t r i n g
0000012
This is also very efficient as it will use the system call truncate().
This seems to do the trick based on this thread:
perl -pi -e 's/\x1b&p[0-9]X\n//g' ~/test.txt
(I am a perl beginner as well - any comments would be appreciated).

TCL logging - unwanted lines in the log file

Here you can see an output of "cat tcl.log":
Discovered serial numbers for slot 1 olt 1:
sernoID Vendor Serial Number sernoID Vendor Serial Number
5 ZNTS 032902A6
And that's how it looks in VIM:
^MDiscovered serial numbers for slot 1 olt 1:
^MsernoID Vendor Serial Number sernoID Vendor Serial Number
^M<SPACE> for next page, <CR> for next line, A for all, Q to quit^H ^H^H ^H^...
5 ZNTS 032902A6
I don't mind the ^M and ^H characters, I know how to get rid of them. The problem is that for some reason my C++ program (unlike cat) is seeing the line starting with "< SPACE >". What can I do about it? I'm using the fstream library to read the log file and I want it to ignore the line I mentioned. I tried to do something like this:
std::ofstream logFinal("logFinal");
std::ifstream log("tcl.log");
std::string temp;
while (std::getline(log, temp)){
if (temp.find("SPACE") != std::string::npos){
temp = "";
}
logFinal << temp << std::endl;
}
But for some reason it doesn't find any "SPACE" in the temp variable. It looks like the "< SPACE >" is some kind of a special character of which I've never heard about.
You're obtaining that log file from/via some sort of program that does paging. (It could be buried inside things; these things happen.) That paging program prints a message like this at the end of a page:
<SPACE> for next page, <CR> for next line, A for all, Q to quit
The <SPACE> is just part of some message with human-readable text; it's seven very ordinary characters. HOWEVER, the ^H that follow it are more interesting, as they're really backspace characters; it's where the preceding characters are deleted again to make way for the next line of real output.
The easiest way (assuming you're on — or have easy access to — a Unix/Linux system) is to feed that log file through col -b (the col program with the -b option, to do backspace elimination). Check out this little cut-n-paste from a shell session:
bash$ echo -e 'abc\b\b\bdef'
def
bash$ echo -e 'abc\b\b\bdef' | od -c
0000000 a b c \b \b \b d e f \n
0000012
bash$ echo -e 'abc\b\b\bdef' | col -b | od -c
0000000 d e f \n
0000004
(The \b should be the same as ^H in your log file.)

How can I swap two lines using sed?

Does anyone know how to replace line a with line b and line b with line a in a text file using the sed editor?
I can see how to replace a line in the pattern space with a line that is in the hold space (i.e., /^Paco/x or /^Paco/g), but what if I want to take the line starting with Paco and replace it with the line starting with Vinh, and also take the line starting with Vinh and replace it with the line starting with Paco?
Let's assume for starters that there is one line with Paco and one line with Vinh, and that the line Paco occurs before the line Vinh. Then we can move to the general case.
#!/bin/sed -f
/^Paco/ {
:notdone
N
s/^\(Paco[^\n]*\)\(\n\([^\n]*\n\)*\)\(Vinh[^\n]*\)$/\4\2\1/
t
bnotdone
}
After matching /^Paco/ we read into the pattern buffer until s// succeeds (or EOF: the pattern buffer will be printed unchanged). Then we start over searching for /^Paco/.
cat input | tr '\n' 'ç' | sed 's/\(ç__firstline__\)\(ç__secondline__\)/\2\1/g' | tr 'ç' '\n' > output
Replace __firstline__ and __secondline__ with your desired regexps. Be sure to substitute any instances of . in your regexp with [^ç]. If your text actually has ç in it, substitute with something else that your text doesn't have.
try this awk script.
s1="$1"
s2="$2"
awk -vs1="$s1" -vs2="$s2" '
{ a[++d]=$0 }
$0~s1{ h=$0;ind=d}
$0~s2{
a[ind]=$0
for(i=1;i<d;i++ ){ print a[i]}
print h
delete a;d=0;
}
END{ for(i=1;i<=d;i++ ){ print a[i] } }' file
output
$ cat file
1
2
3
4
5
$ bash test.sh 2 3
1
3
2
4
5
$ bash test.sh 1 4
4
2
3
1
5
Use sed (or not at all) for only simple substitution. Anything more complicated, use a programming language
A simple example from the GNU sed texinfo doc:
Note that on implementations other than GNU `sed' this script might
easily overflow internal buffers.
#!/usr/bin/sed -nf
# reverse all lines of input, i.e. first line became last, ...
# from the second line, the buffer (which contains all previous lines)
# is *appended* to current line, so, the order will be reversed
1! G
# on the last line we're done -- print everything
$ p
# store everything on the buffer again
h

Resources