Occasionally I use the bash command to replace string in previous command:
^foo^bar
Today I wanted to make the replacement in the following line for replacing all occurrences of checkbox with `radio:
$ git mv _product_checkbox_buttons.html.erb _product_checkbox_button.html.erb
$ ^checkbox^radio
git mv _product_radio_buttons.html.erb _product_checkbox_button.html.erb
So it only replaces the first occurrence. How do I make it replace all occurrences?
bash --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
Copyright (C) 2007 Free Software Foundation, Inc.
man bash says ^old^new is equivalent to !!:s/old/new/. You want !!:gs/old/new/ to globally replace.
An easy way of doing this would be:
fc -s checkbox=radio
I have an alias r defined in my .bashrc:
alias r='fc -s'
Then what you want to do becomes:
r checkbox=radio
See my answer to "hidden features of bash" question for details.
Related
I have installed some GNU packages on my macOS Sierra, which include bash, coreutils, which, etc. Now I can use which -a bash | xargs -I % echo % "--version" | sh to check all version info of bash, but there is no separation between two version info:
$ which -a bash | xargs -I % echo % "--version" | sh
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
# There should be one or more blank lines as separation.
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
I tried ... echo -e % "--version\n" ..., but it can't work. Please help me, thanks.
For a bit more control of the output, use a loop:
which -a bash |
while read cmd; do
printf '%s\t----------\n' "$cmd"
command "$cmd" --version
echo
done
I'd write this as follows:
IFS=: read -r -a path_entries <<<"$PATH"
find "${path_entries[#]}" -maxdepth 1 -name bash -type f -exec '{}' --version ';'
Note:
No use of which. This isn't a shell builtin in bash, so it gives you none of the benefits you'd get from type (ie. awareness of functions and aliases); and because its output is line-oriented, it can't unambiguously emit all possible filenames (such as those containing newlines -- which, yes, are valid).
No reliance on line-oriented content, generally. When the placeholder ({}) is passed as a single token, find -exec substitutes the exact filename returned for that token. Because the domain of possible argv values (all valid C strings, which is to say, no strings containing NULs) matches the domain of possible filenames on common systems, this avoids introducing potential bugs.
No generating text and piping that to an interpreter. This is an extremely error-prone technique, inducing shell-injection vulnerabilities: Consider what would happen if a PATH contained /tmp/$(rm -rf ~), and that directory contained a bash executable.
I am working with: s3-bash, when I run it in my local environment (OS X 10.10.1) I don't have any problems, when I try to run it on a ubuntu server 14.04.1 I get the following error:
./s3-common-functions: line 66: temporaryFiles: unbound variable
./s3-common-functions: line 85: temporaryFiles: unbound variable
I've looked at the s3-common-functions script and the variable looks to be initialized properly (as an array):
# Globals
declare -a temporaryFiles
But there is a note in the comment, and I'm sure if it's related:
# Do not use this from directly. Due to a bug in bash, array assignments do not work when the function is used with command substitution
function createTemporaryFile
{
local temporaryFile="$(mktemp "$temporaryDirectory/$$.$1.XXXXXXXX")" || printErrorHelpAndExit "Environment Error: Could not create a temporary file. Please check you /tmp folder permissions allow files and folders to be created and disc space." $invalidEnvironmentExitCode
local length="${#temporaryFiles[#]}"
temporaryFiles[$length]="$temporaryFile"
}
There appears to be a bash behaviour change at play here.
Found by kojiro: CHANGES
hhhh. Fixed a bug that caused `declare' and `test' to find variables that
had been given attributes but not assigned values. Such variables are
not set.
$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
-bash: tF: unbound variable
You can use declare -a tF=() on the newer bash versions to work around this.
$ declare -a tF=()
$ echo "${#tF[#]}"
0
Bash can substitute an empty value into unset variables using a dash.
set -u
my_array=()
printf "${my_array[#]-}\n"
This specific example will print nothing, but it will not give you an unbound variable error either.
Stolen from Bash: Error `Unbound variable' when appending to empty array1.
However, when used for a loop, you get one iteration (not zero) then:
for i in "${my_array[#]-}"
do
echo $i
done
Expectation might be to get zero iterations.
It could be fixed this way then:
[ ${#my_array[#]} -gt 0 ] &&
for i in "${my_array[#]}"
do
echo $i
done
Changed declaration of array for temporaryfiles
declare -a temporaryFiles
to:
temporaryFiles=()
Why this is different / non-function in ubuntu 14.04.1 Linux 3.13.0-32-generic x86_64 versus OS X I am not sure ?
find $fullfolder -type f |
while read fullfile
do
filename=$(basename "$fullfile")
ext=$([[ $filename = *.* ]] && printf %s ${filename##*.} || printf 'NONE')
arr+=($ext)
echo ${#arr[#]}
done
echo ${#arr[#]}
Why does the ${#arr[#]} inside the for loop produce the correct result but the one outside gives 0?
I have successfully implemented bash completions for my custom commands when the parameters contained no special characters in them, with the compgen mechanism:
current=${COMP_WORDS[COMP_CWORD]}
all=$(_get_all_items)
COMPREPLY=( $(compgen -W "$all" -- $current) )
...
complete -F _prog_compl prog
I use the same approach to complete items which start with a colon character: :first, :second, ... But it fails to show me / autocomplete them. I tried escaping colons with backslashes which didn't work either. How should I escape colons in completion?
The items are starting with colon, let's say: :first, :second. If I write progname and start with a colon like this:
$ progname :<Tab here after colon>
I see no completion but two colons ("::") - one of them automatically added to the line I type. If I convert the colon to an ordinary character (let's say 'b') I get completion exactly like I want: bfirst, bsecond...
It is relevant to note that when I press tab it places another ":" next to my previously inserted colon and it becomes "::".
$ bash --version
GNU bash, version 4.1.10(2)-release (i486-slackware-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Additionally, I have made some experiments in the shell which yield:
$ compgen -W ":aaaa5 :gb2 :cd3" -- ":"
:aaaa5
:gb2
:cd3
Yet it strangely puts another ":" to my sole ":" and makes it "::" after I type : on the command line.
$ complete
complete -F _alp_compl alp.sh
complete -o nospace -F __dbus_send dbus-send
complete -F _myprog_compl myprog
complete -o nospace -F __gdbus gdbus
complete -o nospace -F __gsettings gsettings
After consulting help-bash#gnu.org I got an answer:
The colon breaks words for the completion system (look at the
description of the COMP_WORDBREAKS shell variable), so when you type
progname :[TAB]
the completion system gets an empty word to complete. If all of the
possible completions have `:' as the longest common prefix, then the
completion system will insert the colon into the line.
And my COMP_WORDBREAKS search yield me an answer from stackoverflow.
After fixing (my missing) /etc/bash_completion file from debian bash completion page, now, my colon-initiated completion is working as my will.
I have an ini file similar to this
[test]
foo=bar
and if we call this ini file as test1.ini
How do I change the value of foo to foobarbaz for example using shell script.
I have tried the following and it doesn't work for me. I don't see the updated changes in the ini file. how do I write it?
sed "/^foo=/s/=.*/=foobarbaz/" < test1.ini
Do you have any other suggestions
I personally use a more elaborated sed command, as the same option might appear in several different sections:
sh$ sed -i.bak '/^\[test]/,/^\[/{s/^foo[[:space:]]*=.*/foo = foobarbaz/}' test1.ini
# ^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# make a in the right perform the substitution
# *backup* section as you want
And as a safety net, I would add:
sh$ diff test1.ini{,.bak}
2c2
< foo = foobarbaz
---
> foo=bar
To have the file updated, use the -i option of sed:
sed -i "/^foo=/s/=.*/=foobarbaz/" test1.ini
From man sed:
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
So you can also do
sed -i.bak "/^foo=/s/=.*/=foobarbaz/" test1.ini
Crudini
Crudini is a command line interface to .ini files.
Solution
crudini --set test1.ini test foo foobarbaz
Or, more generally,
crudini --set FILENAME.INI SECTION PARAMETER VALUE
Details
Written in 2013 by Pádraig Brady, crudini has become a standard solution in the UNIX toolkit in the years since this question was first asked. Many GNU/Linux distributions prepackage crudini as part of the OS, making installation simple (e.g., apt install crudini). It also works on Apple MacOS and Microsoft Windows.
I have used my own custom sed scripts in the past, but crudini is easier, more powerful, and less likely to accidentally destroy my files.
See the crudini homepage for more details.
Dangers
While crudini is safer than sed for most people, it is not perfect.
Crudini does not have sed's -i~ option to automatically create a backup file when operating in-place.
It is way too easy to delete an entire section.
crudini --set config.ini foo x=3 will blithely create a line that says, x=3 = .
Its handling of lists (comma separated values) is naive.
To clarify, I am looking for a way to perform a global search and replace on the previous command used. ^word^replacement^ only seems to replace the first match.
Is there some set option that is eluding me?
Try this:
$ echo oneone
oneone
$ !!:gs/one/two/ # Repeats last command; substitutes 'one' --> 'two'.
twotwo
This solution uses Bash Substring Replacement:
$ SENTENCE="1 word, 2 words";echo "${SENTENCE//word/replacement}"
1 replacement, 2 replacements
Note the use of the double slashes denotes "global" string replacement.
This solution can be executed in one line.
Here's how to globally replace a string in a file named "myfile.txt":
$ sed -i -e "s/word/replacement/g" myfile.txt
Blending my answer here with John Feminella's you can do this if you want an alias:
$alias dothis='`history -p "!?monkey?:gs/jpg/png/"`'
$ls *.jpg
monkey.jpg
$dothis
monkey.png
The !! only does the previous command, while !?string? matches the most recent command containing "string".
A nasty way to get around this could be something like this:
Want to echo BAABAA rather than BLABLA by swapping L's for A's
$ echo "BLABLA"
BLABLA
$ `echo "!!" | sed 's/L/A/g'`
$(echo "echo "BLABLA" " | sed 's/L/A/g')
BAABAA
$
Unfortunately this technique doesn't seem to work in functions or aliases.
this question has many dupes and one elegant answer only appears in this answer of user #Mikel in unix se
fc -s pat=rep
this bash builtin is documented under the chapter 9.2 Bash History Builtins
In the second form, command is re-executed after each instance of pat
in the selected command is replaced by rep. command is interpreted the
same as first above.
A useful alias to use with the fc command is r='fc -s', so that typing
‘r cc’ runs the last command beginning with cc and typing ‘r’
re-executes the last command (see Aliases).
I test it on SUSE 10.1.
"^word^replacement^" doesn't work, while "^word^replacement" works well.
for a instance:
linux-geek:/home/Myworks # ls /etc/ld.so.conf
/etc/ld.so.conf
linux-geek:/home/Myworks # ^ls^cat
cat /etc/ld.so.conf
/usr/X11R6/lib/Xaw3d
/usr/X11R6/lib
/usr/i486-linux-libc5/lib=libc5
/usr/i386-suse-linux/lib
/usr/local/lib
/opt/kde3/lib
/opt/gnome/lib
include /etc/ld.so.conf.d/*.conf
linux-geek:/home/Myworks #