This is an overkill version, handling every usually invisible byte for the low 128 except NULL, just for the educational value. A file can be named almost anything, but it can be referenced as ./'name' even if it start with -, unless it contains ', and that can be escaped "'".
Single quoting is most literal, has no metacharacters but itself, is faster for the shell to process, and should always be the first choice.
Unfortuantely, single and double quotes concatenated are hard to read in this font. In a single quoted string like my inline single quoted string as sed script, a single quote is single-double-single-double-single, single to get out of single quoting, add a single in double quotes, and single to get back into single quoting.
This code makes a script for you to review before running in the target dir. The script makes a new dir of files to review before moving them in place of the originals. Making a new dir means there is no risk until you replace the files manually. If duplicate names are generated, you may want to tinker with the list of acceptable file name characters, or just hack the output script and rerun.
(
echo '#!/usr/bin/ksh
rm -rf /tmp/rename_out.$LOGNAME
mkdir /tmp/rename_out.$LOGNAME
'
ls *.pdf| sed '
s/'"'"'/"&"/g
' | sed '
:loop
/\.pdf$/!{
N
s/\n/'"'"'?'"'"'/
b loop
}
p
s/"'"'"'"/_/g
s/'"'"'?'"'"'/_/g
s/[^A-Za-z0-9_.]/_/g
s/___*/_/g
' | sed '
N
s/\(.*\)\n/cp -p '.\/\1' \/tmp\/rename_out.'$LOGNAME'/
'
chmod u+x /tmp/rename_out_$LOGNAME.sh
) >/tmp/rename_out_$LOGNAME.sh
Narrative: Using parentheses to concatenate output as follows into a script in /tmp with your id in the name:
echo commands to build a ksh script that will first:
Destroy any old tmp output dir,
Make empty new tmp output dir.
Generate cp commands to make files with old content and new names:
list all pdf files in the current dir and pass (one file per line unless line feed in file name) to sed #1, which
wraps any ' in ", and then pipe carries that to sed #2, which
if no .pdf suffix on line, loops picking up all of any file name with linefeeds in it, and in place of the embedded linefeed puts the one character glob ? outside anticipated single quotes,
spits out this as the odd (source name) line to sed #3,
reworks that line to build the target name:
replace the embedded single quotes with underscode,
replace the embedded linefeeds with underscore,
replace every character not a letter, number, underscore or dot to be one underscore,
replace multiple underscores with one,
and passes it on to sed #3 as even lines,
sed #3 gets the odd-even pair of lines in the buffer,
change the two lines to cp -p ./'first_line' /tmp/outdir/second_line
changes the generated script to user executable.