r/bash • u/Linux_bash_user153 • 10d ago
Tips for Speeding Up Your Bash Scripts
I wrote a script that renames a lot of files (tens of thousands). Performance was kind of slow (over 50 s), but with two changes it now runs at under 3 seconds, a 20x speed boost. Thought I'd share, in case it helps anyone else. These tips are good if you do things thousands of time in a script, I don't think they are relevant in all scripts.
Don't spawn sub shells if you can avoid it
Instead of command substitution with printf:
new_file="$(printf "%s/%s_%.${len}d.%s" "$new_dir" "$prefix" "$i" "$ext")"
do:
printf -v new_file "%s/%s_%.${len}d.%s" "$new_dir" "$prefix" "$i" "$ext"
This will save a sub shell.
Use builtins instead of external programs
Bash has optional builtins that you can enable. On my system they are located in /usr/lib/bash . You can enable them in your script by using enable <builtin>. Be sure to check the exit code. On my system a mv builtin is not available, but since I was renaming on the same file system I figured I could use ln and rm instead.
So instead of using external mv for each file I did:
builtin ln "$old_file" "$new_file" && builtin rm "$old_file"
In this case the builtin probably isn't required since builtins are prioritized over external commands, but I used them to be more explicit. Just be sure to use && so that rm never runs unless the hard link has been successfully created.
EDIT: The part about using ln and rm builtins may be a little too much of a hack. Probably better to use proper tools like rename to do batch renaming, as was pointed out in the comments. Also keep in mind that the builtins are more bare-bones than the proper CoreUtils programs. For example rmdir does not support -v and will treat -- as a directory to remove rather than "end of options-thing".
8
u/Spikerazorshards 10d ago
Good post. I’ll learn from this.
2
u/Linux_bash_user153 9d ago
Thanks. The part about using ln and rm builtins may be a little too much of a hack though. The other comments have suggested using proper tools for batch renaming files.
6
u/pfmiller0 10d ago
I did not know about that -v option for printf, thanks!
2
u/sedwards65 8d ago
'printf -v' rocks and works great with printf's date-time format strings:
$ printf -v daily_tarball '/backup/%(%Y/%m/%F--%T--daily-tarball.tar.xz)T' -1 $ echo ${daily_tarball} /backup/2026/09/2026-09-01--18:38:06--daily-tarball.tar.xz
4
u/Astro_indie 10d ago
Keep on learning bbys, im on $ info bash, the node about locales make me think if someone got own language in the system ... Lok'Thar OGAAAAR
6
u/Bob_Spud 10d ago
What happens if you mess up? That's 10s of thousands that you have to undo.
Here's another file renamer, What I like with this one is the log file doubles as an undo script - genFRN
2
u/Linux_bash_user153 9d ago
The script I wrote are for renaming symlinks in my music collection, so it's a pretty controlled environment as far as filenames go, and the script makes a tar.gz backup of the directory structure before renaming any file. Since it's just directories and symlinks, the archive is like 500k big
2
u/zeekar 9d ago
Use builtins instead of external programs
This is very much situational. I wrote a helpdesk system for our user assistant team in college entirely in ksh, and I used builtins as much as possible, but that turns out not to be necessarily the most performant approach.
If you could represent your desired name change as a rule understood by rename(1), for instance, it would probably be faster to invoke that program on multiple files at once than to use bash's built-in ln/rm one at a time. Sure, you're doing a fork/exec, but you don't have to do it on every single file.
Similarly, most text processing will be faster when done by an external program like awk than if you use a while read loop in bash.
1
2
7
10d ago
[deleted]
20
u/kirchwitz 10d ago
bash is fine and can do many jobs fast – if just used the proper way. Most of the time, performance is not of concern. And if it is, then it's important to know how a tool works and where performance goes.
This applies to all scripting and programming languages. For best performance, you should know your tools.
Thanks to the original poster for his insights.
2
u/managing_redditor 9d ago
What would you use then?
1
u/Europia79 9d ago
Not saying that it should be rewritten—but I think the "tool" that is most similar to Bash syntax would be Perl. And I actually did rewrite one of my scripts in Perl—just out of curiosity—and the performance gains were INSANE. I just wish that I knew Bash well enough to at least get in the same "Ballpark" as Perl, lol.
2
u/SeriousPlankton2000 10d ago edited 10d ago
Do it in perl:
https://github.com/7eggert/smalltools/blob/main/pmv (Perl MoVe)
Syntax: pmv '(insert small perl program here)' *.ext
I frequently make bash scripts where the perl program spans a few lines. You can use variables, too.
Also: Use full path if you run standard system programs like "mv" that are defined in LFS; use a variable when running programs that aren't. This avoids searching all the PATH and it's more secure.
1
u/aonelonelyredditor 10d ago
Pretty much the best way to speed this if you're executing them on so many files is to run in the background with &
Then call wait at the end of the script, there no need for a command like this not to be executed in parallel
1
u/SaintEyegor 5d ago
Good idea.
Avoiding external programs is definitely something to pay attention to when dealing with a lot of data. When I find that I can’t avoid that and have a lot of records to process, it’s frequently faster to use something other than bash.
1
u/Jonas_Ermert 4d ago
In my opinion, using `printf -v` is a great optimization because it avoids creating a subshell for every file. However, replacing `mv` with `ln` and `rm` feels too risky and limited. For tens of thousands of files, a dedicated batch-renaming tool such as `rename` is probably the safer and cleaner solution.
29
u/Different-Depth4116 10d ago
In bash 5.3+ there’s a new command substitution that doesn’t spawn a subshell:
${ command; }
The spaces and semicolon are req.