How can I remove all text after a character, in this case a colon (":"), in bash? Can I remove the colon, too? I have no idea how to.
7 Answers
In Bash (and ksh, zsh, dash, etc.), you can use parameter expansion with %
which will remove characters from the end of the string or #
which will remove characters from the beginning of the string. If you use a single one of those characters, the smallest matching string will be removed. If you double the character, the longest will be removed.
$ a='hello:world'
$ b=${a%:*}
$ echo "$b"
hello
$ a='hello:world:of:tomorrow'
$ echo "${a%:*}"
hello:world:of
$ echo "${a%%:*}"
hello
$ echo "${a#*:}"
world:of:tomorrow
$ echo "${a##*:}"
tomorrow

- 28,495
- 9
- 107
- 102

- 346,391
- 90
- 374
- 439
-
14This is native shell string manipulation, so no additional processes will be spawned. Reference [Bash Parameter Substitution](http://tldp.org/LDP/abs/html/parameter-substitution.html#PARAMSUBREF), [Bash String Manipulation](http://tldp.org/LDP/abs/html/string-manipulation.html) and [Better Bash Scripting](http://robertmuth.blogspot.jp/2012/08/better-bash-scripting-in-15-minutes.html) – Dodzi Dzakuma Apr 14 '14 at 03:13
-
@denniswilliamson How could I deleted 2 characters after a specific string like `hello:world` will become `hello:wo` by using `${a%:*}`? – 3kstc Jun 25 '16 at 13:05
-
1Great answer, thanks a lot, but it will be nice to also add `"${a##*:}"` for getting only `tomorrow` =) – avtomaton Nov 28 '16 at 01:40
-
Nice. How should I nest these commands? I tried `a=path/to/file_v1.ext; echo "${a##*/%_*}"`, but I didn't get the desired `file` – Sibbs Gambling Dec 16 '16 at 04:42
-
@SibbsGambling: They can't be nested. They have to be done as a sequence of separate operations. – Dennis Williamson Dec 16 '16 at 18:21
-
I am trying to remove _(digit)_ from _word(digit)_ to get only _word_, when using `str="word(5)"`, the `str="${str%(*}"` actually seems to add some character, instead of removing "_(5)_". Any ideas what's wrong? – Nazar Jul 12 '17 at 16:01
-
@Nazar: You need to escape the open parenthesis, like so: str="${str%\(*}" – Eldad Mor May 06 '18 at 12:58
-
@EldadMor: Your escaping didn't show up, probably because you didn't enclose your code in backticks. – Dennis Williamson May 06 '18 at 15:42
-
Is there another possible variation to generate hello: – openCivilisation Mar 10 '19 at 12:05
-
@openCivilisation: What are you looking for? – Dennis Williamson Mar 10 '19 at 13:12
-
the example ```echo "${a%%:*}"``` generates ```hello```. its really close to what I'm after, I'd just like to include the ```:``` if it exists as well to produce ```hello:``` – openCivilisation Mar 10 '19 at 13:43
-
1@openCivilisation: You probably need to use regular expression matching then: `a='hello:world:of:tomorrow'; pattern=''^([^:]*:).*$'`; [[ $a =~ $pattern ]]; echo "${BASH_REMATCH[1]}"`. That matches only if there is a colon. If you want that to be optional, a different pattern would have to be used. An explanation of Bash regexes (and regexes in general) is beyond the scope of these comments. You can find other questions that discuss this or post your own. – Dennis Williamson Mar 10 '19 at 16:41
-
What if you have a string "Hello: world: this: is: me: again" and you want to truncate everything after the LAST occurrence of the ":", given that you do NOT know how many ":" characters are in the string. So here I would want the output to be "Hello: world: this: is: me". How would you do that? – kp123 Aug 28 '19 at 21:20
-
1@kp123: It's the first example in my answer. The second time I show it (where "tomorrow" is removed), it's almost exactly the situation you're asking about. – Dennis Williamson Aug 28 '19 at 21:29
-
doesn't work for me: `echo ${"https://www.rt.com/news/510303-thailand-outbreak-coronavirus-testing/?utm_source=rss&utm_medium=rss&utm_campaign=RSS"%\?*}` – chovy Dec 25 '20 at 08:47
-
@chovy afaik you need to use variable `a='https://www.rt.com/news/510303-thailand-outbreak-coronavirus-testing/?utm_source=rss&utm_medium=rss&utm_campaign=RSS'; echo ${a%\?*}` – JakubKnejzlik Aug 18 '21 at 16:00
An example might have been useful, but if I understood you correctly, this would work:
echo "Hello: world" | cut -f1 -d":"
This will convert Hello: world
into Hello
.

- 1,787
- 3
- 18
- 27

- 5,405
- 3
- 34
- 46
-
6`cut` works, but I Dennis' answer is better and more flexible. Does anyone know if it spawns a new process like `cut`? – JoBu1324 Feb 20 '14 at 18:06
-
4It's better to use Bash's built-in parameter expansion features than generating subshells running tools like `basename` and `cut`, see Dennis's answer below – Louis Maddox Jun 14 '16 at 17:32
-
2`cut` can read from stdin, so it is better especially when you have a very long string that you need to process, like the contents of a file. – Sahas Apr 26 '17 at 08:34
-
2And how would this be applied to `hello: world`. This is half an answer. – basickarl Oct 09 '17 at 12:20
-
It can be done using subsitution syntax also like {var/:/''}, but works only in bash.. – Mahesh Feb 14 '19 at 11:40
-
What if you have a string "Hello: world: this: is: me: again" and you want to truncate everything after the LAST occurrence of the ":", given that you do NOT know how many ":" characters are in the string. So here I would want the output to be "Hello: world: this: is: me". How would you do that? – kp123 Aug 28 '19 at 21:19
I know some solutions:
# Our mock data:
A=user:mail:password
- With awk and pipe:
$ echo $A | awk -v FS=':' '{print $1}'
user
- Via bash variables:
$ echo ${A%%:*}
user
- With pipe and sed:
$ echo $A | sed 's#:.*##g'
user
- With pipe and grep:
$ echo $A | egrep -o '^[^:]+'
user
- With pipe and cut:
$ echo $A | cut -f1 -d\:
user

- 121
- 1
- 5
egrep -o '^[^:]*:'

- 158,093
- 24
- 286
- 300
-
1
-
3`egrep` is grep with `-E`. It makes no difference here from regular `grep`. `-o` instructs grep to print out only the part of the line that matches the expression. `^` anchors the match to the start of a line. `[^:]*` matches zero or more characters that are not the `:` character. `:` matches the character `:`. – cdhowie Dec 29 '16 at 16:16
trim off everything after the last instance of ":"
grep -o '^.*:' fileListingPathsAndFiles.txt
and if you wanted to drop that last ":"
grep -o '^.*:' file.txt | sed 's/:$//'
@kp123: you'd want to replace :
with /
(where the sed colon should be \/
)

- 484
- 1
- 3
- 13

- 129
- 5
-
1The only correct answer. As this answer removes *after* a specific character given, the other answers remove the character aswell. – basickarl Jun 16 '21 at 15:50
Let's say you have a path with a file in this format:
/dirA/dirB/dirC/filename.file
Now you only want the path which includes four "/". Type
$ echo "/dirA/dirB/dirC/filename.file" | cut -f1-4 -d"/"
and your output will be
/dirA/dirB/dirC
The advantage of using cut is that you can also cut out the uppest directory as well as the file (in this example), so if you type
$ echo "/dirA/dirB/dirC/filename.file" | cut -f1-3 -d"/"
your output would be
/dirA/dirB
Though you can do the same from the other side of the string, it would not make that much sense in this case as typing
$ echo "/dirA/dirB/dirC/filename.file" | cut -f2-4 -d"/"
results in
dirA/dirB/dirC
In some other cases the last case might also be helpful. Mind that there is no "/" at the beginning of the last output.

- 185
- 3
- 14
-
2What if you have a directory "/resources/views/admin/users/relationships/posts.blade.php" and you want to truncate everything after the LAST occurrence of the "/", given that you do NOT know how many "/" characters are in the string. So here I would want the output to be "/resources/views/admin/users/relationships/" (or even /resources/views/admin/users/relationships without the trailing slash). How would you do that? – kp123 Aug 28 '19 at 21:21
-
1
-
What about if i have a number 19359.364263397103 how do i remove all chars starting with . and all tothe right so all thats left is the left part whats the command look like for that? – Jay Mee Oct 03 '22 at 16:44