-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcustom_loader.sh
3646 lines (3363 loc) · 211 KB
/
custom_loader.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
####################
#
# *** custom_loader.sh *** prepares common configurations and sets up the .custom profile extensions
# - Compatible with most distros (uses apt / yum / dnf / apk package managers as appropriate)
# - Installs a common set of tools, such as vim,
# dpkg apt-file alien \ # apt-file required for searching on 'what provides a package' searches, alien converts packages
# python3.9 python3-pip perl \ # Get latest python/pip and perl if not present on this distro
# cron curl wget pv dos2unix \ # Basic tools, cron is not installed by default on CentOS etc
# git vim zip unzip mount \ # Basic tools, git, full vim package, zip/unzip, mount is not on all systems
# nnn dfc pydf ncdu tree net-tools \ # nnn (more useful than mc), dfc, pdf, ncdu variants
# htop neofetch inxi figlet ) # neofetch/inxi system information tool, apt contains figlet, so try this
# Always run the git config before cloning to fix potential line ending problems, especially on WSL
# git config --global core.autocrlf input
# git clone https://github.com/roysubs/custom_bash
# . <(curl -sS https://raw.githubusercontent.com/roysubs/custom_bash/master/.custom) # To dotsource from github immediately
#
# This script performs some configuration options to make a consistent bash environemt, and then
# installss .custom into the profile ready to be used in interactive shells.
#
####################
# Some WSL / Putty / Linux editing:
# To paste into a Terminal (in Linux, not via Putty), use Ctrl+Shift+V. In Putty, use Shift+Insert.
# Also can use the middle mouse button to paste selected text in a Linux Terminal (i.e. if in a Hyper-V Ubuntu session)
# https://askubuntu.com/questions/734647/right-click-to-paste-in-terminal?newreg=00145d6f91de4cc781cd0f4b76fccd2e
# Useful Toolkits to look through:
# https://www.commandlinefu.com/commands/browse
# Nam Nguyen : https://github.com/gdbtek/ubuntu-cookbooks/blob/master/libraries/util.bash referenced from https://serverfault.com/questions/20747/find-last-time-update-was-performed-with-apt-get
# SSH keys setup : https://github.com/creynoldsaccenture/bash-toolkit
# BEF (Bash Essential Functions) : https://github.com/shoogle/bash-essential-functions/blob/master/modules/bef-filepaths.sh
# Bash-it : https://www.tecmint.com/bash-it-control-shell-scripts-aliases-in-linux/
# https://www.digitalocean.com/community/tutorials/an-introduction-to-useful-bash-aliases-and-functions
# https://gitlab.com/bertrand-benoit/scripts-common
# https://opensource.com/article/19/7/bash-aliases
# https://src-r-r.github.io/articles/essential-bash-commands-to-make-life-easier/
# https://tldp.org/LDP/abs/html/sample-bashrc.html
# https://www.linuxhowtos.org/Tips%20and%20Tricks/
# https://www.quora.com/What-are-some-useful-bash_profile-and-bashrc-tips?encoded_access_token=e46b28e4342944a58cb01681b425df9e&expires_in=5184000&fb_uid=4467565573287648&force_dialog=1&provider=facebook&share=1&success=True#_=_
# https://dev.to/awwsmm/101-bash-commands-and-tips-for-beginners-to-experts-30je
# https://www.addictivetips.com/ubuntu-linux-tips/edit-the-bashrc-file-on-linux/
# https://crunchbang.org/forums/viewtopic.php?id=1093
# https://wiki.linuxquestions.org/wiki/Scripts#Command_Line_Trash_Can cli trash can function, large compressed files, Kerberos, JVMs
# https://wiki.linuxquestions.org/wiki/Bash_tips
# https://www.cyberciti.biz/tips/bash-aliases-mac-centos-linux-unix.html
# http://philip.vanmontfort.be/bestanden/linux/bashrc
# https://github.com/erwanjegouzo/dotfiles/blob/master/.bash_profile
# https://serverfault.com/questions/3743/what-useful-things-can-one-add-to-ones-bashrc?page=1&tab=votes#tab-top
# https://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
# https://github.com/algotech/dotaliases
# https://github.com/dmeekabc/tagaProductized/tree/master/iboaUtils IBOA Utils, alias tools
# https://www.grymoire.com/Unix/Sed.html Excellent Sed Guide
# https://blog.sanctum.geek.nz/series/unix-as-ide/ Excellent Guide on shell usage
# https://www.cyberciti.biz/tips/bash-aliases-mac-centos-linux-unix.html
# https://blog.ssdnodes.com/blog/13-smart-terminal-tools-to-level-up-your-linux-servers/ # Some very useful tools
# tldr: Read simplified instructions on common terminal commands
# how2: Get answers to your terminal questions
# z: Jump to ‘frecently’ used places
# trash-cli: Put files in the trash
# nnn: Manage your files visually
# bat: View files with syntax highlighting
# pomo: A Pomodoro timer in your terminal
# fselect: Find files with the speed of SQL
# exa: List files with more features (and colors) than ls
# peco: Get grep-like filtering with interactivity
# has: Do you 'has' the dependencies you need?
# progress: See how much longer mv, dd, cp, and more will take
# mackup: Sync and restore your application settings
# transfer.sh: Share files directly from the command line
# https://www.tecmint.com/12-top-command-examples-in-linux/
# https://www.dedoimedo.com/computers/new-cool-list-linux.html
# https://www.xmodulo.com/useful-cli-tools-linux-system-admins.html
# The [[ ]] construct is the more versatile Bash version of [ ]. This is the extended test command, adopted from ksh88.
# Using the [[ ... ]] test construct, rather than [ ... ] can prevent many logic errors in scripts. For example, the &&, ||, <, and > operators work within a [[ ]] test, despite giving an error within a [ ] construct.
# Problem with 'set -e', so have removed. It should stop on first error, but instead it kills the WSL client completely https://stackoverflow.com/q/3474526/
# If you want to run apt-get without having to supply a sudo password, just edit the sudo config file to allow that. (Replace “jfb” in this example with your own login).
# jfb ALL=(root) NOPASSWD: /usr/bin/apt-get
# https://www.cyberciti.biz/open-source/30-cool-best-open-source-softwares-of-2013/
# alias gitupdate='(for l in `find . -name .git | xargs -i dirname {}` ; do cd $l; pwd; git pull; cd -; done)' # https://stackoverflow.com/questions/4083834/what-are-some-interesting-shell-scripts
# alias backup='rsync -av ~/Documents [email protected]: --delete --delete-excluded --exclude-from=/Users/myusername/.rsync/exclude --rsh="ssh"'
####################
#
# Setup print_header() and exe() functions
#
####################
hh=/tmp/.custom # This is the location of all helper scripts, could change this location
[ -d $hh ] || mkdir $hh
### print_header() will display up to 3 arguments as a simple banner
print_header() {
printf "\n\n####################\n"
printf "#\n"
if [ "$1" != "" ]; then printf "# $1\n"; fi
if [ "$2" != "" ]; then printf "# $2\n"; fi
if [ "$3" != "" ]; then printf "# $3\n"; fi
printf "#\n"
printf "####################\n"
}
### exe() will display a command and then run that same command, so you can see what is about to be run
# https://stackoverflow.com/questions/2853803/how-to-echo-shell-commands-as-they-are-executed
# By default, the following exe() will run run unattended, i.e. will show the command and then execute immediately
# However, if "y" is chosen, the exe() function is updated to display the command, then display a pause before running as follows:
# exe() { printf "\n\n"; echo "\$ ${@/eval/}"; read -e -p "Press 'Enter' to continue..."; "$@"; }
# ToDo (might not be required): modify this so that it displays a y/n after each command so can skip some and continue on to other commands.
# https://stackoverflow.com/questions/29436275/how-to-prompt-for-yes-or-no-in-bash
exe() { printf "\n"; echo "\$ ${@/eval/}"; "$@"; }
####################
#
print_header "Start common cross-distro configuration steps" "and setup common tools (.custom / .vimrc / .inputrc)" "without creating complex changes in .bashrc etc"
#
####################
####################
#
print_header "Also configure some basic useful settings in" ".inputrc / .vimrc / and etc/sudoers.d/custom_rules"
#
####################
[ -f /etc/lsb-release ] && RELEASE="$(cat /etc/lsb-release | grep DESCRIPTION | sed 's/^.*=//g' | sed 's/\"//g')" # Debian / Ubuntu and variants
[ -f /etc/redhat-release ] && RELEASE=$(cat /etc/redhat-release); # RedHat / Fedora / CentOS, contains "generic release" information
[ -f /etc/os-release ] && RELEASE="$(cat /etc/os-release | grep ^NAME= | sed 's/^.*=//g' | sed 's/\"//g')" # This now contains the release name
printf "OS : $RELEASE"
if grep -qEi "WSL2" /proc/version &> /dev/null ; then printf " (Running in WSL2)" # Ubuntu now lists WSL version in /proc/version
elif grep -qEi "Microsoft" /proc/version &> /dev/null ; then printf " (Running in WSL)" # Other distros don't list WSL version but do have "Microsoft" in the string
fi
printf "\n\n"
# Dotsourcing custom_loader.sh is required to update the running environment, but causes
# problems with exiting scripts, since exit 0 / exit 1 will quit the bash shell since that
# is what you are running when you are dotsourcing. e.g. This will close the whole shell:
# if [ $(pwd) == $HOME ]; then echo "custom_loader.sh should not be run from \$HOME"; exit 0; fi
# So, instead, we'll just notify the user and let them take action:
# echo "\$BASH_SOURCE = $BASH_SOURCE" # $BASH_SOURCE gets the source being used, always use this when dotsourcing
if [ $(pwd) == $HOME ]; then echo "It is not advised to run custom_loader.sh from \$HOME"; read -e -p "Press 'Ctrl-C' to exit script..."; fi
if [ -f "$HOME/custom_loader.sh" ]; then read -e -p "Do not run custom_loader.sh \$HOME"; read -e -p "Press 'Ctrl-C' to exit script..."; fi
if [ ! -f "./custom_loader.sh" ]; then read -e -p "Script should only be run when currently in same folder as custom_loader.sh location. 'cd' to the correct folder and rerun."; read -e -p "Press 'Ctrl-C' to exit script..."; fi
# if the path starts "/" then it is absolute
# if the path starts with "~/" or "./" or just the name of the subfolder then it is not absolute
# if the path is not absolute, add $(pwd) to get the full path
### Note that read -e -p will be ignored when running via curl, so this is actually ok since it is ok to
### run from internet even when in $HOME
# Array example, splitting a path:
# pwd_arr=($(dirname \"$0\" | tr "/", "\n")) # split path script is running from into array
# for i in "${pwd_arr[@]}"; do echo $i; done # loop through elements of the array
# pwd_last= ${pwd_arr[-1]} # get the last element of the array
echo "Default is to skip confirmation, but may require a sudo password"
# if [ -f ~/.config/.custom-locale ];
# echo "Input locale to set for this system (this will be saved in ~/.config/.custom-locale)"
# echo "Possible locales are: EN, GB, US, NL, FR, IT, etc"
# [[ "$(read -e -p 'Input locale code to set for this system? > '; echo $REPLY)" == []* ]] && exe() { printf "\n\n"; echo "\$ ${@/eval/}"; read -e -p "Press 'Enter' to continue..."; "$@"; }
# fi
[[ "$(read -e -p 'Confirm each configutation step? [y/N]> '; echo $REPLY)" == [Yy]* ]] && exe() { printf "\n\n"; echo "\$ ${@/eval/}"; read -e -p "Press 'Enter' to continue..."; "$@"; }
####################
#
print_header "Find package manager and run package/distro updates"
#
####################
manager=
type apt &> /dev/null && manager=apt && DISTRO="Debian/Ubuntu"
type yum &> /dev/null && manager=yum && DISTRO="RHEL/Fedora/CentOS"
type dnf &> /dev/null && manager=dnf && DISTRO="RHEL/Fedora/CentOS" # $manager=dnf will be default if both dnf and yum are present
type zypper &> /dev/null && manager=zypper && DISTRO="SLES"
type apk &> /dev/null && manager=apk && DISTRO="Alpine"
# apk does not require 'sudo' or a '-y' to install
if [ "$manager" = "apk" ]; then
INSTALL="$manager add"
else
INSTALL="sudo $manager install -y"
fi
# Only install each a binary from that package is not already present on the system
check_and_install() { type $1 &> /dev/null && printf "\n$1 is already installed" || exe $INSTALL $2; }
# e.g. type dos2unix &> /dev/null || exe sudo $manager install dos2unix -y
[[ "$manager" = "apk" ]] && check_and_install sudo sudo # Just install sudo on Alpine for script compatibility
echo -e "\n\n>>>>>>>> A variant of '$DISTRO' was found, so will use"
echo -e ">>>>>>>> the '$manager' package manager for setup tasks."
echo ""
printf "> sudo $manager update -y\n> sudo $manager upgrade -y\n> sudo $manager dist-upgrade -y\n> sudo $manager install ca-certificates -y\n> sudo $manager autoremove -y\n"
# Note 'install ca-certificates' to allow SSL-based applications to check for the authenticity of SSL connections
# Need to make sure that 'needsrestarting' is present on CentOS to check if a reboot is required, or we find another tool
if type dnf &> /dev/null 2>&1; then
type needsrestarting &> /dev/null || sudo dnf install yum-utils -y
fi
# I could 'source .custom' at the top of this script and then call 'pt' and 'updistro'. This is
# not good though; if there is a bug in '.custom', then this script will fail, and variables from
# there might interfere here. Just keep a copy of the 'updistro' function in this script also and
# make sure both are in sync.
pt() {
# 'package tool', arguments are a list of package names to try. e.g. pt vim dfc bpytop htop
# Determine if packages are already installed, fetch distro package list to see what is available, and then install the difference
# If '-auto' or '--auto' is in the list, will install without prompts. e.g. pt -auto vlc emacs
# Package names can be different in Debian/Ubuntu vs RedHat/Fedora/CentOS. e.g. python3.9 in Ubuntu is python39 in CentOS
arguments="$@"; isinrepo=(); isinstalled=(); caninstall=(); notinrepo=(); toinstall=""; packauto=0; endloop=0;
[[ $arguments == *"--auto"* ]] && packauto=1 && arguments=$(echo $arguments | sed 's/--auto//') # enable switch and remove switch from arguments
[[ $arguments == *"-auto"* ]] && packauto=1 && arguments=$(echo $arguments | sed 's/-auto//') # must do '--auto' before '-auto' or will be left with a '-' fragment
mylist=("$arguments") # Create array out of the arguments. mylist=(python3.9 python39 mc translate-shell how2 npm pv nnn alien angband dwarf-fortress nethack-console crawl bsdgames bsdgames-nonfree tldr tldr-py bpytop htop fortune-mod)
# if declare -p $1 2> /dev/null | grep -q '^declare \-a'; then echo "The input \$1 must be an array"; return; fi # Test if the input is an array
type apk &> /dev/null && manager="apk" && apk list &> /dev/null > /tmp/all-repo.txt && dnf list installed &> /dev/null > /tmp/all-here.txt && divider=""
type apt &> /dev/null && manager="apt" && apt list &> /dev/null > /tmp/all-repo.txt && apt list --installed &> /dev/null > /tmp/all-here.txt && divider="/"
type dnf &> /dev/null && manager="dnf" && dnf list -y &> /dev/null > /tmp/all-repo.txt && dnf list installed &> /dev/null > /tmp/all-here.txt && divider=""
for x in ${mylist[@]}; do grep "^$x$divider" /tmp/all-repo.txt &> /dev/null && isinrepo+=($x); done # find items available in repo
# echo -e "These are in the repo: ${isinrepo[@]}\n\n" # $(for x in ${isinrepo[@]}; do echo $x; done)
for x in ${mylist[@]}; do grep "^$x$divider" /tmp/all-here.txt &> /dev/null && isinstalled+=($x); done # find items already installed
notinrepo+=(`echo ${mylist[@]} ${isinrepo[@]} | tr ' ' '\n' | sort | uniq -u `) # get the diff from two arrays, jave have to consider the right arrays to use here # different answer here: https://stackoverflow.com/a/2315459/524587
echo ""
[[ ${isinrepo[@]} != "" ]] && echo "These packages exist in the $manager repository: ${isinrepo[@]}" # $(for x in ${isinstalled[@]}
[[ ${isinstalled[@]} != "" ]] && echo "These packages are already installed on this system: ${isinstalled[@]}" # $(for x in ${isinstalled[@]}
[[ ${notinrepo[@]} != "" ]] && echo "These packages do not exist in the repository: ${notinrepo[@]}" # $(for x in ${isinstalled[@]}
echo 3
caninstall+=(`echo ${isinrepo[@]} ${isinstalled[@]} | tr ' ' '\n' | sort | uniq -u `) # get the diff from two arrays (use "${}" if spaces in array elements) # https://stackoverflow.com/a/28161520/524587
if [ $packauto = 1 ]; then
if (( ${#caninstall[@]} )); then sudo $manager install -y ${caninstall[@]} # Test the number of elements, if non-zero then enter the loop
else echo -e "\nNo selected packages can be installed. Exiting ...\n"
fi
return
fi
while [ $endloop = 0 ]; do
caninstall=(Install-and-Exit)
caninstall+=(`echo ${isinrepo[@]} ${isinstalled[@]} | tr ' ' '\n' | sort | uniq -u `) # get the diff # https://stackoverflow.com/questions/2312762/compare-difference-of-two-arrays-in-bash#comment52200489_28161520
if [[ ${caninstall[@]} = "Install-and-Exit" ]]; then echo -e "\nNo new packages exist in the repository to be installed. Exiting ...\n"; return; fi
COLUMNS=12
[[ $toinstall != "" ]] && echo -e "\n\nCurrently selected packages: $toinstall"
echo -e "\n\nSelect a package number to add to the install list.\nTo install the selected packages and exit the tool, select '1'.\n"
printf -v PS3 '\n%s ' 'Enter number of package to install: '
select x in ${caninstall[@]}; do
toinstall+=" $x "
toinstall=$(echo $toinstall | sed 's/Install-and-Exit//' | tr ' ' '\n' | sort -u | xargs) # https://unix.stackexchange.com/a/353328/441685
if [ $x == "Install-and-Exit" ]; then endloop=1; fi
break
done
done
if [[ $toinstall = *[!\ ]* ]]; then # https://unix.stackexchange.com/a/147109/441685
echo -e "\n\n\nAbout to run: sudo $manager install $toinstall\n\n"
read -p "Press Ctrl-C to skip installation or press any key to install the package(s) ..."
sudo $manager install -y $toinstall
else
echo -e "\nNo selected packages can be installed. Exiting ...\n"
fi
}
updistro() { # Self-contained function, no arguments, perform all update/upgrade functions for the current distro
type apt &> /dev/null && manager=apt && DISTRO="Debian/Ubuntu"
type yum &> /dev/null && manager=yum && DISTRO="RHEL/Fedora/CentOS"
type dnf &> /dev/null && manager=dnf && DISTRO="RHEL/Fedora/CentOS" # $manager should be dnf if both dnf and yum are present
type zypper &> /dev/null && manager=zypper && DISTRO="SLES"
type apk &> /dev/null && manager=apk && DISTRO="Alpine"
function separator() { echo -e "\n>>>>>>>>\n"; }
function displayandrun() { echo -e "\$ ${@/eval/}\n"; "$@"; } # Show a command to run, and then run it, useful for showing progress during scripts
printf "\nCheck updates:"
echo -e "\n\n>>>>>>>> The '$DISTRO' package manager was found, so will"
echo -e ">>>>>>>> use the '$manager' package manager for setup tasks."
if [ "$manager" == "apt" ]; then separator; displayandrun sudo apt --fix-broken install -y; fi # Check and fix any broken installs, do before and after updates
if [ "$manager" == "apt" ]; then separator; displayandrun sudo apt dist-upgrade -y; fi
if [ "$manager" == "apt" ]; then separator; displayandrun sudo apt-get update --ignore-missing -y; fi # Note sure if this is needed
if [ "$manager" == "apt" ]; then type apt-file &> /dev/null && separator && displayandrun sudo apt-file update; fi # update apt-file cache but only if apt-file is present
if [ "$manager" == "apk" ]; then
separator; displayandrun apk update; separator; displayandrun apk upgrade
else
separator; displayandrun sudo $manager update
separator; displayandrun sudo $manager upgrade # Note that on dnf, update/upgrade are the same command
separator; displayandrun sudo $manager install ca-certificates -y # To allow SSL-based applications to check for the authenticity of SSL connections
separator; displayandrun sudo $manager autoremove
fi
if [ "$manager" == "apt" ]; then separator; displayandrun sudo apt --fix-broken install -y; fi # Check and fix any broken installs, do before and after updates
if [ -f /var/run/reboot-required ]; then
echo "A reboot is required (/var/run/reboot-required is present)." >&2
echo "Re-run this script after reboot to check." >&2
return
fi
echo ""
}
function getLastUpdate()
{
if [[ $manager == "apt" ]]; then local updateDate="$(stat -c %Y '/var/cache/apt')"; fi # %Y time of last data modification, in seconds since Epoch
if [[ $manager == "dnf" ]]; then local updateDate="$(stat -c %Y '/var/cache/dnf/expired_repos.json')"; fi # %Y time of last data modification, in seconds since Epoch
local nowDate="$(date +'%s')" # %s seconds since 1970-01-01 00:00:00 UTC
echo $((nowDate - updateDate)) # simple arithmetic with $(( ))
}
function runDistroUpdate()
{
local updateInterval="${1}" # An update interval can be specifide as $1, otherwise default to a value below
local lastUpdate="$(getLastUpdate)"
if [[ -z "$updateInterval" ]] # "$(isEmptyString "${updateInterval}")" = 'true'
then
updateInterval="$((24 * 60 * 60))" # Adjust this to how often to do updates, setting to 24 hours in seconds
fi
updateIntervalReadable=$(printf '%dh:%dm:%ds\n' $((updateInterval/3600)) $((updateInterval%3600/60)) $((updateInterval%60)))
if [[ "${lastUpdate}" -gt "${updateInterval}" ]] # only update if $updateInterval is more than 24 hours
then
print_header "apt updates will run as last update was more than ${updateIntervalReadable} ago"
# Handle EPEL (Extra Packages for Enterprise Linux) and PowerTools, fairly essential for hundreds of packages like htop, lynx, etc
# To remove EPEL (normally only do this if upgrading to a new distro of CentOS, e.g. from 7 to 8)
# sudo rpm -qa | grep epel # Check the epel version installed
# sudo rpm -e epel-release-x-x.noarch # Remove the installed epel, x-x is the version
# sudo rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm # Install the latest epel
# yum repolist # check if epel is installed
# if type dnf &> /dev/null 2>&1; then exe sudo $manager -y upgrade https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm; fi
if type dnf &> /dev/null 2>&1; then
if [[ $(rpm -qa | grep epel-release) ]]; then
echo ""
echo "$DISTRO : EPEL Repository is already installed"
else
sudo dnf install epel-release
fi
if [[ $(dnf repolist | grep powertools) ]]; then
echo "$DISTRO : PowerTools Repository is already installed"
else
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --set-enabled powertools # Note that this must be lowercase, 'PowerTools' fails
fi
fi
updistro
else
local lastUpdate="$(date -u -d @"${lastUpdate}" +'%-Hh %-Mm %-Ss')"
print_header "Skip apt-get update because its last run was less than ${updateIntervalReadable} ago" "Last apt-get update run was ${lastUpdate} ago"
fi
}
runDistroUpdate
if [ -f /var/run/reboot-required ]; then
echo ""
echo "A reboot is required (/var/run/reboot-required is present)." # >&2
echo "If running in WSL, can shutdown with: wsl.exe --terminate \$WSL_DISTRO_NAME"
echo "Re-run this script after reboot to finish the install."
. .custom # In case this is first run of custom-loader.sh, source in .custom anyway to make those aliases and functions available
return # Script will exit here if a reboot is required
fi
if [[ "$manager" == "dnf" ]] || [[ "$manager" == "yum" ]]; then
needsReboot=$(needs-restarting -r &> /dev/null 2>&1; echo $?) # Supress the output message from needs-restarting (from yum-utils)
if [[ $needsReboot == 1 ]]; then
echo "Note: A reboot is required (by checking: needs-restarting -r)."
echo "Re-run this script after reboot to finish the install."
. .custom # In case this is first run of custom-loader.sh, source in .custom anyway to make those aliases and functions available
return # Script will exit here if a reboot is required
fi
fi
####################
#
print_header "Check and install small/essential packages"
#
####################
# I could 'source .custom' at the top of this script and then call 'pt'.
# Decided not to do this though, as if there is a bug in '.custom', then this script will fail, so just
# keep a copy of the 'pt' function in this script also (has been added in previous section).
nowDate="$(date +'%s')" # %s seconds since 1970-01-01 00:00:00 UTC
updateDate=$nowDate
[ -f /tmp/all-repo.txt ] && updateDate="$(stat -c %Y '/tmp/all-repo.txt')" # %Y time of last data modification, in seconds since Epoch
lastUpdate=$((nowDate - updateDate)) # simple arithmetic with $(( ))
updateInterval="$((24 * 60 * 60))" # Adjust this to how often to do updates, setting to 24 hours in seconds
updateIntervalReadable=$(printf '%dh:%dm:%ds\n' $((updateInterval/3600)) $((updateInterval%3600/60)) $((updateInterval%60)))
echo $lastUpdate
echo $updateInterval
if [[ "${lastUpdate}" -gt "${updateInterval}" ]]
then
echo ""
# echo Put tasks that should only run once every 24 hours here
fi
packages=( dpkg apt-file alien \ # apt-file required for searching on 'what provides a package' searches, alien converts packages
python3.9 python3-pip perl \ # Get latest python/pip and perl if not present on this distro
cron curl wget pv dos2unix \ # Basic tools, cron is not installed by default on CentOS etc
git vim zip unzip mount \ # Basic tools, git, full vim package, zip/unzip, mount is not on all systems
nnn dfc pydf ncdu tree net-tools \ # nnn (more useful than mc), dfc, pdf, ncdu variants
htop neofetch inxi figlet ) # neofetch/inxi system information tool, apt contains figlet, so try this
pt -auto ${packages[@]} # 'pt' will create a list of valid packages from those input and then installs those
echo ""
echo ""
echo ""
####################
#
print_header "Download extended fonts for 'figlet'"
#
####################
# Download and setup extended figlet fonts to /usr/share/figlet (requires elevation)
# http://www.jave.de/figlet/fonts.html
# http://www.figlet.org/examples.html
# Note that some of these fonts cannot show parts of the time output
# exe sudo bash -c '
# wget -P /usr/share/figlet/ "http://www.jave.de/figlet/figletfonts40.zip"
# unzip -d /usr/share/figlet/ /usr/share/figlet/figletfonts40.zip # unzip to -d destination
# mv -f /usr/share/figlet/fonts/* /usr/share/figlet/ # move all fonts back into the main folder (force)
# rmdir /usr/share/figlet/fonts'
echo "# Download and setup figlet extended fonts"
# [ ! -f /tmp/figletfonts40.zip ] && exe sudo wget -P /tmp/ "http://www.jave.de/figlet/figletfonts40.zip"
# [ ! -f /usr/share/figlet/univers.flf ] && exe sudo unzip -od /usr/share/figlet/ /tmp/figletfonts40.zip # unzip to destination -d, with overwrite -o
# [ -d /usr/share/figlet/fonts ] && exe sudo mv -f /usr/share/figlet/fonts/* /usr/share/figlet/ # move all fonts back into the main folder (force)
# [ -d /usr/share/figlet/fonts ] && exe sudo rmdir /usr/share/figlet/fonts
if [ ! -f /usr/share/figlet/univers.flf ]; then # Use existence of this one font file to decide
sudo mkdir -p /usr/share/figlet/fonts
[ ! -f /tmp/figletfonts40.zip ] && exe sudo wget -nc --tries=3 -T20 --restrict-file-names=nocontrol -P /tmp/ "http://www.jave.de/figlet/figletfonts40.zip"
[ -f /tmp/figletfonts40.zip ] && exe sudo unzip -od /usr/share/figlet/ /tmp/figletfonts40.zip # unzip to destination -d, with overwrite -o
[ -d /usr/share/figlet/fonts ] && exe sudo mv -f /usr/share/figlet/fonts/* /usr/share/figlet/ # move all fonts back into the main folder (force)
[ -d /usr/share/figlet/fonts ] && exe sudo rmdir /usr/share/figlet/fonts
[ -f /tmp/figletfonts40.zip ] && exe sudo rm /tmp/figletfonts40.zip # cleanup
fi
####################
#
print_header "PowerShell (pwsh) Shell for Linux"
#
####################
echo "Setup the PowerShell shell on Linux (start the shell with 'pwsh')"
echo ""
echo "=====> For Ubuntu"
# wget -q https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb"
echo "wget -q https://packages.microsoft.com/config/ubuntu/\$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb"
echo "dpkg -i packages-microsoft-prod.deb"
echo "apt-get update -y"
echo "apt-get install powershell -y"
echo ""
echo "=====> For CentOS"
echo "curl https://packages.microsoft.com/config/rhel/8/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo"
echo "sudo dnf install powershell"
echo ""
echo ""
echo ""
# up.sh is a more complete "cd up" script. I don't really need this, just define u1, u2, u3, u4, u5 shortcuts in .custom
# shortcut creates a db of locations, similar to my PowerShell function, again, now sure if I want to use this, but handy to know about
# curl --create-dirs -o ~/.config/up/up.sh https://raw.githubusercontent.com/shannonmoeller/up/master/up.sh # echo 'source ~/.config/up/up.sh' >> ~/.bashrc # For .custom
# [[ ! -d ~/.config/shortcut ]] && git clone https://github.com/zakkor/shortcut.git ~/.config/shortcut # install.sh will create ~/.scrc for key-pairs and /usr/local/bin/shortcut.sh
if [ ! $(which bat) ]; then # if 'bat' is not present, then try to get it
####################
#
print_header "Download 'bat' (syntax highlighted replacement for 'cat') manually to a known working version"
#
####################
# Following task is to get the latest package from a non-repo site, then optionally convert it (with alien) to a compatible
# format, and then install it. Some useful methods here that can be used elsewhere:
# - Finding the latest download link on a site
# - String manipulations to get components of link, filename, extension etc
# - Using \K lookbehnid functionality in grep by using Perl regex mode (-P)
# - Feed a variable into grep with '<<<' instead of a file
# - Using 'alien' to (try and) convert a .deb into a .rpm to install on CentOS
# This is not currently working on CentOS, *but*, creating the .rpm on Ubuntu then moving that to CentOS works
# It generates some errors:
# file / from install of bat-musl-0.18.3-2.x86_64 conflicts with file from package filesystem-3.8-6.el8.x86_64
# file /usr/bin from install of bat-musl-0.18.3-2.x86_64 conflicts with file from package filesystem-3.8-6.el8.x86_64
# But forcing it to install did work and probably does not break anything, i.e. see here: https://stackoverflow.com/questions/27172142/conflicts-with-file-from-package-filesystem-3-2
# sudo rpm -i --force bat-musl-0.18.3-2.x86_64.rpm
# - Note on extracting substrings in bash: https://www.baeldung.com/linux/bash-substring
echo "# Download and setup 'bat' so that .custom can alias 'cat' to use 'bat' instead"
echo "# This provides same functionality as 'cat' but with colour syntax highlighting"
# When we get the .deb file, the install syntax is:
# sudo dpkg -i /tmp/bat-musl_0.18.3_amd64.deb # install
# sudo dpkg -r bat # remove
# Can alternatively use pygmentize: https://www.gilesorr.com/blog/pygmentize-less.html, but bat -f (--force-colorization) is better
# We need to determine the latest download link for our needs, starting from this url
# Need to look at /releases/ even though the downloads are under /releases/download/ as the links are here
bat_releases=https://github.com/sharkdp/bat/releases/
content=$(wget $bat_releases -q -O -)
# -q quiet. -O option allows speficying a filename to download to; specify - to get the dump onto standard output (which we collect into $content)
# Need to then look for first link ending "_amd64.deb", e.g. href="/sharkdp/bat/releases/download/v0.18.3/bat_0.18.3_amd64.deb"
# content=$(curl -L google.com) # You could also use curl, -L as the page might have moved to get from new location, --location option helps with this.
# grep -P uses Perl regular expressions to allow for \K lookbehinds. https://stackoverflow.com/questions/33573920/what-does-k-mean-in-this-regex
# echo 'hello world' | grep 'hello \K(world)' # Will only match 'world' if and only if it is preceded by 'hello ' but will not include the 'hello ' part in the match.
# grep -oP 'href="\K/sharkdp/[^"]*_amd64\.deb' # matches href=" (then drops it from the match), then matche /sharkdp/ and any number of chars *except* " , and then match _amd_64.deb
firstlink=$(grep -oP 'href="/sharkdp/bat/releases/\K[^"]*_amd64\.deb' <<< "$content" | head -1)
# Note how to feed a variable into grep with '<<<' instead of a file
DL=$bat_releases$firstlink
ver_and_filename=$(grep -oP 'https://github.com/sharkdp/bat/releases/download/\K[^"]*\.deb' <<< "$DL") # v0.18.3/bat-musl_0.18.3_amd64.deb
IFS='/' read -ra my_array <<< "$ver_and_filename" # for i in "${my_array[@]}"; do echo $i; done
ver=${my_array[0]}
filename=${my_array[1]}
IFS='.' read -ra my_array <<< "$filename"
extension=${my_array[-1]}
extension_with_dot="."$extension
filename_no_extension=${filename%%${extension_with_dot}*} # https://stackoverflow.com/questions/62657224/split-a-string-on-a-word-in-bash
# various ways to get name without extension https://stackoverflow.com/questions/12152626/how-can-i-remove-the-extension-of-a-filename-in-a-shell-script
echo -e "$DL\n$ver_and_filename\n$ver\n$filename\n$extension\n$filename_no_extension"
[ ! -f /tmp/$filename ] && exe wget -nc --tries=3 -T20 --restrict-file-names=nocontrol -P /tmp/ $DL # Timeout after 20 seconds
# Try to use 'alien' to create a .rpm from a .deb: alien --to-rpm <name>.deb # https://forums.centos.org/viewtopic.php?f=54&t=75913
# I can get this to work when running alien on Ubuntu, but alien fails with errors when running on CentOS.
# Need to be able to do it from the CentOS system however to automate the install in this way.
# In the end, looks like the 'alien' setup is not required as 'bat' will install into CentOS using dpkg(!)
# But keep this code as might need for other packages to convert them.
### if [ "$manager" == "dnf" ] || [ "$manager" == "yum" ]; then
### # Don't need to worry about picking the latest version as unchanged since 2016
### ALIENDL=https://sourceforge.net/projects/alien-pkg-convert/files/release/alien_8.95.tar.xz
### ALIENTAR=alien_8.95.tar.xz
### ALIENDIR=alien-8.95 # Note that the extracted dir has "-" while the downloaded file has "_"
### exe wget -P /tmp/ $ALIENDL
### tar xf $ALIENTAR
### cd /tmp/$ALIENDIR
### exe sudo $manager install perl -y
### exe sudo $manager install perl-ExtUtils-Install -y
### exe sudo $manager install make -y
### sudo perl Makefile.PL
### sudo make
### sudo make install
### cd ..
### sudo alien --to-rpm $filename # bat-musl_0.18.3_amd64.deb
### # sudo rpm -ivh $FILE.rpm # bat-musl-0.18.3-2.x86_64.rpm : note that the name has changed
### # Ideally, we should create a folder, create the output in there, then grab the name from there, since the name can change
### fi
which bat &> /dev/null || exe sudo dpkg -i /tmp/$filename # if the 'bat' command is present, do nothing, otherwise install with dpkg
# sudo dpkg -r bat # to remove 'bat' if required
# Also note that 'bat' is part of the 'bacula-console-qt' package but that is 48 MB for an entire backup tool
# https://linuxconfig.org/how-to-install-deb-file-in-redhat-linux-8
rm /tmp/$filename
fi
####################
#
print_header "Install exa (possible replacement for ls)"
#
####################
# WARNING!!! Could break system!
# sudo wget -P /tmp/ "http://ftp.nl.debian.org/debian/pool/main/g/glibc/libc6-udeb_2.32-4_amd64.udeb"
# sudo dpkg --install /tmp/libc6-udeb_2.32-4_amd64.udeb
# sudo wget -P /tmp/ "http://ftp.nl.debian.org/debian/pool/main/g/gcc-11/libgcc-s1_11.2.0-8_amd64.deb"
# sudo dpkg --install /tmp/libgcc-s1_11.2.0-8_amd64.deb
# sudo wget -P /tmp/ "http://ftp.nl.debian.org/debian/pool/main/r/rust-exa/exa_0.10.1-1_amd64.deb"
# sudo dpkg
####################
#
print_header "WSL Utilities"
#
####################
echo "https://github.com/wslutilities/wslu"
echo ""
echo ""
echo ""
# sudo apk add wslu
# Arch Linux
# AUR version of wslu is pulled due to that it violated its policy.
#
# Download the latest package from release and install using the command: sudo pacman -U *.zst
#
# CentOS/RHEL
# Add the repository for the corresponding Linux distribution:
#
# CentOS 7: sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/home:/wslutilities/CentOS_7/home:wslutilities.repo
# CentOS 8: sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/home:/wslutilities/CentOS_8/home:wslutilities.repo
# Red Hat Enterprise Linux 7: sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/home:/wslutilities/RHEL_7/home:wslutilities.repo
# Then install with the command sudo yum install wslu.
#
# Debian / Kali
# You can install wslu with the following command:
#
# sudo apt install gnupg2 apt-transport-https
# wget -O - https://pkg.wslutiliti.es/public.key | sudo tee -a /etc/apt/trusted.gpg.d/wslu.asc
# echo "deb https://pkg.wslutiliti.es/debian buster main" | sudo tee -a /etc/apt/sources.list
# sudo apt update
# sudo apt install wslu
# sudo apt install gnupg2 apt-transport-https
# wget -O - https://pkg.wslutiliti.es/public.key | sudo tee -a /etc/apt/trusted.gpg.d/wslu.asc
# echo "deb https://pkg.wslutiliti.es/kali kali-rolling main" | sudo tee -a /etc/apt/sources.list
# sudo apt update
# sudo apt install wslu
# Fedora
# sudo dnf copr enable wslutilities/wslu
# sudo dnf install wslu
####################
#
print_header "Update .bashrc so that it will load .custom during any interactive login sessions"
#
####################
# Object here is to carefully inject the required loader lines into ./bashrc. Intent is to only ever add two
# lines to ~/.bashrc, and only ever at the end of the file, so they will be pruned and re-attached to end of
# the file if required.
# (( grep for $thing in $file )) , or , (( echo $thing | tee --append $thing ))
# i.e. $expression1 || $expression2 where $expression2 = echo $thing | tee --append $thing
# 'tee --append' is better than '>>' in general as it also permits updating protected files.
# e.g. echo "thing" >> ~/.bashrc # fails as cannot update protected file (since the '>>' is not elevated)
# sudo echo "thing" >> ~/.bashrc # also fails as the 'echo' is elevated, but '>>' is not(!)
# But: echo "thing" | sudo tee --append /etc/bashrc # works as the 'tee' operation is elevated correctly.
# https://linux.die.net/man/1/grep
# https://stackoverflow.com/questions/3557037/appending-a-line-to-a-file-only-if-it-does-not-already-exist
# Could alternatively use sed for everything:
# https://unix.stackexchange.com/questions/295274/grep-to-find-the-correct-line-sed-to-change-the-contents-then-putting-it-back
# Backup ~/.custom
if [ ! -d $hh ]; then mkdir -p $hh; fi
if [ -f ~/.custom ]; then
echo "Create Backup : $hh/.custom_$(date +"%Y-%m-%d__%H-%M-%S").sh"
cp ~/.custom $hh/.custom_$(date +"%Y-%m-%d__%H-%M-%S").sh # Need to rename this to make way for the new downloaded file
fi
if [ -f ~/.bashrc ]; then
echo "Create Backup : $hh/.bashrc_$(date +"%Y-%m-%d__%H_%M_%S").sh"
exe cp ~/.bashrc $hh/.bashrc_$(date +"%Y-%m-%d__%H_%M_%S").sh # Backup .bashrc in case of issues
fi
### Note: removing GETCUSTOM, as it's a little pushy, and will fail on a non-internet connected system.
# Remove lines to trigger .custom at end of .bashrc (-v show everything except, -x full line match, -F fixed string / no regexp)
# https://stackoverflow.com/questions/28647088/grep-for-a-line-in-a-file-then-remove-the-line
# Remove our .custom from the end of .bashrc (-v show everything except our match, -q silent show no output, -x full line match, -F fixed string / no regexp)
# GETCUSTOM='[ ! -f ~/.custom ] && [[ $- == *"i"* ]] && curl -s https://raw.githubusercontent.com/roysubs/custom_bash/master/.custom > ~/.custom'
# grep -vxF "$GETCUSTOM" $rc > $rctmp.2 && sudo cp $rctmp.2 $rc
HEADERCUSTOM='# Dotsource .custom; download project from: git clone https://github.com/roysubs/custom_bash --depth=1'
RUNCUSTOM='[ -f ~/.custom ] && [[ $- == *"i"* ]] && source ~/.custom'
rc=~/.bashrc
rctmp=$hh/.bashrc_$(date +"%Y-%m-%d__%H-%M-%S").tmp
if [ -f $rc ]; then
grep -vxF "$HEADERCUSTOM" $rc > $rctmp.1 && sudo cp $rctmp.1 $rc # grep to a .tmp file, then copy it back to the original
grep -vxF "$RUNCUSTOM" $rc > $rctmp.3 && sudo cp $rctmp.3 $rc
# If doing this with a system file (e.g. /etc/bashrc), just sudo the 'cp' part. i.e. grep <> /etc/bashrc > $rctmp && sudo cp /etc/bashrc
# After removing our lines, make sure no empty lines at end of file, except for one required before our lines
# Remove trailing whitepsace: https://stackoverflow.com/questions/4438306/how-to-remove-trailing-whitespaces-with-sed
sed -i 's/[ \t]*$//' ~/.bashrc # -i is in place, [ \t] applies to any number of spaces and tabs before the end of the file "*$"
# Removes also any empty lines from the end of a file. https://unix.stackexchange.com/questions/81685/how-to-remove-multiple-newlines-at-eof/81687#81687
sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' ~/.bashrc
echo "" | tee --append ~/.bashrc # Finally, add an empty line back in as a separator before our .custom call lines
# Add lines to trigger .custom to end of .bashrc (-q silent show no output, -x full line match, -F fixed string / no regexp)
echo $HEADERCUSTOM | tee --append ~/.bashrc
echo $GETCUSTOM | tee --append ~/.bashrc
echo $RUNCUSTOM | tee --append ~/.bashrc
fi
### .bash_profile checks ###
# In practice, the usage of the .bash_profile file is the same as the usage for the .bashrc file.
# Most .bash_profile files call the .bashrc file for the user by default. Then why do we have two
# different configuration files? Why can’t we do everything using a single file?
# Well, the short answer is freedom and convenience. The longer answer is as follows: Suppose, you
# wish to run a system diagnostic every time you log in to your Linux system. You can edit the
# configuration file to print the results or save it in a file. But you only wish to see it at
# startup and not every time you open your terminal. This is when you need to use the .bash_profile
# file instead of .bashrc. Also: https://www.golinuxcloud.com/bashrc-vs-bash-profile/
# If ".bash_profile" is ever created, it takes precedence over .bashrc, *even* if it is empty, AND
# .bashrc will be suppressed and will NOT load in this case (just one of the crazy bash rules).
# To avoid this, we need to check for .bash_profile and whether it has contents.
# - If .bash_profile exists and is zero-length, simply delete it.
# - If .bash_profile exists is not zero length, then create a line to dotsource .bashrc so that .custom will also be called.
# - If .bash_profile exists and .bashrc does NOT exist, then add .custom lines to .bash_profile instead of .bashrc
if [ ! -s ~/.bash_profile ]; then # This is specifically only if .bash_profile is zero-length (-z for zero-length is not available on some OS)
echo "Deleting zero-size ~/.bash_profile to prevent overriding .bashrc"
rm ~/.bash_profile &> /dev/null
fi
if [ -s ~/.bash_profile ]; then # Only do this if a greater than zero size file exists
echo "Existing ~/.bash_profile is not empty, so ensure line in ~/.bash_profile loads ~/.bashrc (and so then runs ~/.custom)"
FIXBASHPROFILE='[ -f ~/.bashrc ] && . ~/.bashrc'
grep -qxF "$FIXBASHPROFLIE" ~/.bash_profile || echo "$FIXBASHPROFILE" | tee --append ~/.bash_profile
fi
if [ -s ~/.bash_profile ] && [ ! -f ~/.bashrc ]; then # Only do this if a greater than zero size file exists
echo "Existing ~/.bash_profile is not empty, but ~/.bashrc does not exist. Ensure lines are in ~/.bash_profile to load ~/.custom"
grep -qxF "$HEADERCUSTOM" ~/.bash_profile || echo "$HEADERCUSTOM" | tee --append ~/.bash_profile
grep -qxF "$GETCUSTOM" ~/.bash_profile || echo "$GETCUSTOM" | tee --append ~/.bash_profile
grep -qxF "$RUNCUSTOM" ~/.bash_profile || echo "$RUNCUSTOM" | tee --append ~/.bash_profile
fi
####################
#
print_header "Common changes to .vimrc"
#
####################
# read -e -p "Press 'Enter' to continue ..."; "$@"
# https://topic.alibabacloud.com/article/ubuntu-system-vimrc-configuration-file_3_12_513382.html
# https://linuxhint.com/vimrc_tutorial/
# grep -qxF "$VIMLINE" ~/.vimrc || echo $VIMLINE | sudo tee --append ~/.vimrc
# -q, --quiet, --silent Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option.
# -x, --line-regexp Select only those matches that exactly match the whole line (i.e. like parenthesizing the pattern and then surrounding it with ^ and $ for regex).
# -F, --fixed-strings Interpret PATTERNS as fixed strings, not regular expressions).
#
# First, check if the full line is already in the file. If there, do nothing, then check the ~ version, only add if not there
# ToDo: extend this to check both /etc/vimrc and then ~/.vimrc, possibly a function with parameteres $1=(line to check or add), $2=(/etc/xxx admin file), $3=(~/.vimrc user file)
#
# Note: cannot >> back to the same file being read, so use "| sudo tee --append ~/.vimrc"
# Note: cannot have multiple spaces " " in a line or can't grep -qxF on that as the spaces are stripped
ADDFILE=~/.vimrc
function addToFile() { grep -qxF "$1" $ADDFILE || echo $1 | tee --append $ADDFILE; }
addToFile '" Set simple syntax highlighting that is more readable than the default (also :set koehler)'
addToFile 'color industry'
addToFile '" Disable tabs (to get a tab, Ctrl-V<Tab>), tab stops are 4 chars, indents are 4 chars'
addToFile 'set expandtab tabstop=4 shiftwidth=4'
addToFile '" Allow saving of files as sudo if did not start vim with sudo'
addToFile "cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit"
addToFile '" Set F3 to toggle line numbers on/off'
addToFile 'noremap <F3> :set invnumber<CR>'
addToFile 'inoremap <F3> <C-O>:set invnumber<CR>'
addToFile '" Jump between windows with Ctrl-h/j/k/l'
addToFile 'nnoremap <C-H> <C-W>h'
addToFile 'nnoremap <C-J> <C-W>j'
addToFile 'nnoremap <C-K> <C-W>k'
addToFile 'nnoremap <C-L> <C-W>l'
# ADDLINE='" Set simple syntax highlighting that is more readable than the default (also :set koehler)'
# grep -qxF "$ADDLINE" ~/.vimrc || echo $ADDLINE | tee --append ~/.vimrc
# ADDLINE='color industry'
# grep -qxF "$ADDLINE" ~/.vimrc || echo $ADDLINE | tee --append ~/.vimrc
# Alternative way to auto-elevate vim (not sure if this works)
# ADDLINE='" command W w !sudo tee % >/dev/nullset expandtab tabstop=4 shiftwidth=4'
####################
#
print_header "Common changes to /etc/samba/smb.conf"
#
####################
# ToDo: add here generic changes to make sure that Samba will work, including some default sharing
# Add an entry for the home folder on this environment so that is always available
# Restart the samba service
####################
#
print_header "Common changes to .inputrc"
#
####################
# To verify which code is sent to your terminal when you press a key or a combination of keys,first
# press Ctrl+V and then press on the desired key. This can be different depending upon the terminal,
# such as PuTTY. For example, pressing Ctrl+V then the Home key on my PuTTY terminal:
# ^[[1~
# That means that PuTTY sends the escape character ^[ followed by the string [1~
# After this, .inputrc codes can be defined for these key codes.
# if [ ! -a ~/.inputrc ]; then echo '$include /etc/inputrc' > ~/.inputrc; fi
if [ ! -f ~/.inputrc ]; then touch ~/.inputrc; fi
# Add shell-option to ~/.inputrc to enable case-insensitive tab completion, add this then start a new shell
# echo >> ~/.inputrc
# You’ll be able to browse through your command line history, simply start typing a few letters of the command and then use the arrow up and arrow down keys to browse through your history. This is similar to using Ctrl+r to do a reverse-search in your Bash, but a lot more powerful, and a feature I use every day.
# The .inputrc is basically the configuration file of readline - the command line editing interface used by Bash, which is actually a GNU project library. It is used to provide text related editing features, customized keybindings etc.
ADDFILE=~/.inputrc
function addToFile() { grep -qxF "$1" $ADDFILE || echo $1 | tee --append $ADDFILE; }
# Do not have extra spaces in lines (i.e. between ), as the grep above cannot handle them, so do not align all comments after the command etc
addToFile '$include /etc/inputrc' # include settings from /etc/inputrc
addToFile '# Set tab completion for cd to be non-case sensitive'
addToFile 'set completion-ignore-case On # Set Tab completion to be non-case sensitive'
addToFile '"\e\C-e": alias-expand-line # Expand aliases, with Ctrl-Alt-l, additional to the default "Esc then Ctrl-e"'
addToFile '"\e[5~": history-search-backward # After Ctrl-r, release Ctrl, then PgUp to go backward'
addToFile '"\e[6~": history-search-forward # After Ctrl-r, release Ctrl, then PgDn to go forward'
addToFile '"\C-p": history-search-backward # After Ctrl-r, Ctrl-p to go backward (previous)'
addToFile '"\C-n": history-search-forward # After Ctrl-r, Ctrl-n to go forward (next)'
addToFile '"\eOD": backward-word'
addToFile '"\eOC": forward-word'
# INPUTRC='$include /etc/inputrc' # include settings from /etc/inputrc
# grep -qxF "$INPUTRC" ~/.inputrc || echo $INPUTRC | sudo tee --append ~/.inputrc
# https://superuser.com/questions/241187/how-do-i-reload-inputrc
# https://relentlesscoding.com/posts/readline-transformation/
# https://www.topbug.net/blog/2017/07/31/inputrc-for-humans/
#
# $include /etc/inputrc
# "\C-p":history-search-backward
# "\C-n":history-search-forward
#
# set colored-stats On
# set completion-ignore-case On
# set completion-prefix-display-length 3
# set mark-symlinked-directories On
# set show-all-if-ambiguous On
# set show-all-if-unmodified On
# set visible-stats On
# "\e[A": history-search-backward
# "\e[B": history-search-forward
# "\eOD": backward-word
# "\eOC": forward-word
# "\e[1~": beginning-of-line
# "\e[4~": end-of-line
#
# "\C-i": menu-complete
# set show-all-if-ambiguous on
# update .inputrc
# allow the use of the Home/End keys
# "\e[1~": beginning-of-line
# "\e[4~": end-of-line
# allow the use of the Delete/Insert keys
# "\e[3~": delete-char
# "\e[2~": quoted-insert
# mappings for "page up" and "page down" to step to the beginning/end
# of the history
# "\e[5~": beginning-of-history
# "\e[6~": end-of-history
# alternate mappings for "page up" and "page down" to search the history
# "\e[5~": history-search-backward
# "\e[6~": history-search-forward
# mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
# "\e[1;5C": forward-word
# "\e[1;5D": backward-word
# "\e[5C": forward-word
# "\e[5D": backward-word
# "\e\e[C": forward-word
# "\e\e[D": backward-word
# Rough working:
# Seems that /etc/inputrc is not loaded by default, so have to use "$include /etc/inputrc"
# testforline() { cat $1; cat $2; }
# testforline /etc/inputrc ~/.inputrc "\"\\e\[1\~\": beginning-of-line"
# check for the line in $1 and $2, and only add to $2 if not present in either
####################
#
print_header "Common changes to /etc/sudoers (/etc/sudoers.d/custom-rules)"
#
####################
# First, make sure that /etc/sudoers.d is enabled
custom_rules=/etc/sudoers.d/custom-rules
if ! sudo grep -q '^[@#]includedir /etc/sudoers.d$' /etc/sudoers; then
sudo sed '$a\\n@includedir /etc/sudoers.d' /etc/sudoers > /etc/sudoers.new
[ ! -d /etc/sudoers.d ] && sudo mkdir /etc/sudoers.d
sudo visudo -c -f /etc/sudoers.new && sudo mv /etc/sudoers{.new,}
echo "Create Backup : $hh/sudoers_$(date +"%Y-%m-%d__%H-%M-%S").sh"
sudo cp /etc/sudoers $hh/sudoers_$(date +"%Y-%m-%d__%H-%M-%S").sh
fi
if [ -f $custom_rules ]; then
echo "Create Backup : $hh/sudoers.d/custom_rules_$(date +"%Y-%m-%d__%H-%M-%S").sh"
sudo cp $custom_rules $hh/custom_rules_$(date +"%Y-%m-%d__%H-%M-%S").sh
fi
# echo " sudo visudo --file=/etc/sudoers.d/arash-extra-rules"
# echo "Automating this change will look something like the following, but do not do this as it will"
# echo "break /etc/sudoers in this format (so don't do this!):"
# echo " # sed 's/env_reset$/env_reset,timestamp_timeout=600/g' /etc/sudoers \| sudo tee /etc/sudoers"
if ! [ -f /etc/sudoers.d/customrules ]; then
echo "Automating changes to /etc/sudoers can make a system unbootable so we will"
echo "only change a file under /etc/sudoers.d"
echo "Note that if sudo breaks, you can run 'pkexec visudo' on some distros."
echo "In pkexec visudo, fix any issues, copy in from a backup of /etc/sudoers"
echo "See all options with 'man sudoers'"
echo ""
echo "Add a 10 hr timeout for sudo passwords to be re-entered for home systems:"
echo "Add 'Defaults timestamp_timeout=600' to '/etc/sudoers.d/custom-rules'"
echo "Note that mutiple Default statements on different lines/files will all be added."
if [ ! -f $custom_rules ]; then
sudo touch $custom_rules
sudo chmod 0440 $custom_rules
else
if ! sudo grep -q '^# Defaults can be comma separated, or on multiple statements one per line' $custom_rules; then echo '# Defaults can be comma separated, or on multiple statements one per line' | sudo tee --append $custom_rules; fi
if ! sudo grep -q '^Defaults timestamp_timeout=600' $custom_rules; then echo 'Defaults timestamp_timeout=600' | sudo tee --append $custom_rules; fi
fi
echo ""
fi
sudo visudo -c -f /etc/sudoers # Will check all sudoer files
# timestamp_timeout
# Number of minutes that can elapse before sudo will ask for a passwd again. The timeout may include a fractional component if
# minute granularity is insufficient, for example 2.5. The default is 15. Set this to 0 to always prompt for a password. If set to
# a value less than 0 the user's time stamp will not expire until the system is rebooted. This can be used to allow users to create
# or delete their own time stamps via “sudo -v” and “sudo -k” respectively.
# A note on the use of env_reset (Ubuntu and CentOS both use it, but CentOS restricts it with env_keep from what I can see)
# https://unixhealthcheck.com/blog?id=363
# Programmatically automate changes to sudoers. There are guides for this.
# Nornally, should only use visudo to update /etc/sudoers, so have to be very careful with this one, as can break system
# However, on Ubuntu and many modern distros, fixing a corrupted sudoers file is actually quite easy, and doesn't require
# rebooting or a live CD. To do this via SSH, log in to the machine and run the command pkexec visudo. If you have physical
# access to the machine, SSH is unnecessary; just open a Terminal window and run that pkexec command. Assuming you (or some
# other user) are authorized to run programs as root with PolicyKit, you can enter your password, and then it will run
# visudo as root, and you can fix your /etc/sudoers by running: pkexec visudo
# If you need to edit one of the configuration files in /etc/sudoers.d (which is uncommon in this situation, but possible), use:
# pkexec visudo -f /etc/sudoers.d/filename.
# If you have a related situation where you have to perform additional system administration commands as root to fix the problem
# (also uncommon in this circumstance, but common in others), you can start an interactive root shell with pkexec bash. Generally
# speaking, any non-graphical command you'd run with sudo can be run with pkexec instead.
# (If there is more than one user account on the system authorized to run programs as root with PolicyKit, then for any of those
# actions, you'll be asked to select which one you want to use, before being asked for your password.)
# First, check if this system has a line ending "env_reset" (seems to normally be there in all Ubuntu / CentOs systems)
# SUDOTMP="/tmp/sudoers.$(date +"%Y-%m-%d__%H-%M-%S")"
# sudo cp /etc/sudoers $SUDOTMP
# Require "sudo tee" as /etc/sudoers does not even have 'read' permissiong so "> sudoers.1" would not work
# Can also use "--append" to tee, useful to adding to end of a file, but in this case we do not need
# Add option to view the sudoers file in case it is broken to offer to copy the backup back in
####################
#
print_header "nnn -aA and cd on quit"
#
####################
# https://bleepcoder.com/nnn/631249390/conflicting-alias-and-cd-on-quit
# https://github.com/jarun/nnn/issues/689
# Vim provides built-in commands to print or change the current working directory without needing to exit vim. For example, you can run :cd ~ to change to your home directory or :pwd to print the current working directory. The nnn (n)vim plugin could serve as a much faster alternative to change the current working directory without needing to exit vim. In my case, I want to take advantage of nnn's fzz plugin to quickly change directories within vim. Somebody else raised an issue with the same interest: mcchrish/nnn.vim#57. Why would you want to change directories within vim? Well, there are many reasons. One might be that you want to run vim's builtin :make command, which invokes the Makefile on the current working directory.
#
# I already have a working version. I had to modify both your nnn source code and the nnn vim plugin. @mcchrish agrees that this would be a neat feature to add (mcchrish/nnn.vim#57).
### ####################
### #
### print_header "Dotfile management"
### #
### ####################
### echo "The goal here is to create a folder for all dotfiles, then hard link them and so be able to"
### echo "create a git project that is easy to update / replicate on another system."
### echo "Using this script, and the dotfile project above it: https://github.com/gibfahn/dot/blob/main/link"
### echo ""
### echo ""
### echo ""
####################
####################
####################
####################
####################
#
print_header "HELP FILES : Create various help scripts for notes and tips and alias them in .custom"
#
####################
####################
####################
####################
####################
# Try to build a collection of common notes, summaries, tips, tricks to always be accessible from the console.
# This is a good template for creating help files for various summaries (could also do vim, tmux, etc)
# Note the "" surround $1 in out1() { echo "$1" >> $HELPFILE; } otherwise prefix/trailing spaces will be removed.
# Using echo -e to display the final help file, as printf requires escaping "%" as "%%" or "\045" etc)
# In .custom, we can then simply create aliases if that file exists.
# https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo/65819#65819
# https://www.shellscript.sh/tips/echo/
# Both printf and echo -e have issues, but printf overall is probably better.
# Note escaping \$ \\, and " is little awkward, as requires \\\" (\\ => \ and \" => ").
# ####################