Як видалити залишки білих просторів за допомогою sed?


113

У мене є простий скрипт оболонки, який вилучає з файлу пробіли пробілів. Чи є спосіб зробити цей скрипт більш компактним (без створення тимчасового файлу)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp

2
Ви можете використовувати mvзамість catі rm. Чому ти catвсе одно використовуєш таке? Чому б не використовувати cp?
Призупинено до подальшого повідомлення.

1
Я використав знання, які я навчився з цього питання, щоб створити сценарій оболонки для рекурсивного видалення останнього пробілу .
David Tuite

1
Ваше рішення насправді краще при використанні MinGW з - за помилки в SED на Windows: stackoverflow.com/questions/14313318 / ...
Коди Пірсол


Зауважте, що використання catдля перезапису оригінального файлу замість того mv, що фактично замінить дані у вихідному файлі (тобто не порушить жорсткі посилання). Використання sed -iзапропонованих у багатьох рішеннях цього не зробить. IOW, просто продовжуйте робити те, що ви робите.
Вільям Перселл

Відповіді:


157

Ви можете використовувати в місці опції -iз sedдля Linux і Unix:

sed -i 's/[ \t]*$//' "$1"

Будьте в курсі, що вираз видалить трейлінг tна OSX (ви можете використовувати, gsedщоб уникнути цієї проблеми). Він також може видалити їх і на BSD.

Якщо у вас немає gsed, ось правильний (але важко читаючий) синтаксис sed на OSX:

sed -i '' -E 's/[ '$'\t'']+$//' "$1"

Три однорядкові рядки зрештою об'єднуються в єдиний аргумент / вираз. У bash немає оператора конкатенації, ви просто розміщуєте рядки один за одним, не маючи пробілу між ними.

У $'\t'вирішує , як буквальна вкладку-символ в БАШЕЄВ ( з використанням ANSI-C зі посиланням ), так що язичок правильно зчеплений в вираз.


1
На моїй машині з'являється таке, що я не можу оновити: sed: Not a recognized flag: i
javaPlease42,

2
хм. його також баггі в тому сенсі, що він видалить усі затримані "t" s :)
Good Person

2
"sed: Не розпізнаний прапор: i -" Це відбувається в OSX. Вам потрібно додати розширення для файлу резервної копії після -i на Mac. наприклад: sed -i .bak 's / [\ t] * $ //' $ 1
Еймон Бустардо

1
@GoodPerson Якщо ви не жартували, ви, ймовірно, забудете уникнути t:) \t- це вкладка для тих, хто, можливо, ще не знає.
Шон Аллред

2
@SeanAllred не жартував: його абсолютно зламали, якщо ви не користуєтеся GNU sed (який зламаний так багато інших способів)
Good Person

59

Принаймні, на Гірському Леві, відповідь Віктора також видалить персонаж 't', коли він знаходиться в кінці рядка. Наступні виправлення, які вирішують проблему:

sed -i '' -e's/[[:space:]]*$//' "$1"

1
Мій sed також хотів -Eзазначити "розширені (сучасні) регулярні вирази"
Джаред Бек

Працює як шарм на OS X. Дякую дуже.
jww

1
Відповідь кодиданта має ту саму проблему і в OS X (зараз macOS). Це єдине рішення на цій платформі.
Франклін Ю

@JaredBeck Шахта sedна Ель-Капітані не зробила.
Франклін Ю

19

Завдяки кодидації за пропозицію -iваріанту.

Наступна команда вирішує проблему на Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"


7
Як говорить @acrollet, ви не можете користуватися \tз sed, крім GNU sed, і це трактується як буквальний лист t. Здається, що команда спрацьовує, ймовірно, тому, що tу вашому файлі немає пробілів TAB, а не в кінці речення. Використання ''без вказівки резервного суфікса не рекомендується.
Scruutinizer


5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2

1
Гей, це саме те, що мені було потрібно! Інші розміщені рішення sed мали проблему інтеграції з присвоєнням змінної трубопроводу (і на трубопроводі, і з трубопроводом ...) у моєму скрипті bash, але ваше працювало нестандартно.
Ерік Л.

4

У мене є сценарій у моєму .bashrc, який працює під OSX та Linux (лише для удару!)

function trim_trailing_space() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will trim (in place) trailing spaces in the given file (remove unwanted spaces at end of lines)"
    echo "Usage :"
    echo "$FUNCNAME file"
    return
  fi
  local file=$1
  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    sed -E -i ''  's/[[:space:]]*$//' $file
  else
    sed -i  's/[[:space:]]*$//' $file
  fi
}

до якого я додаю:

SRC_FILES_EXTENSIONS="js|ts|cpp|c|h|hpp|php|py|sh|cs|sql|json|ini|xml|conf"

function find_source_files() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will list sources files (having extensions $SRC_FILES_EXTENSIONS)"
    echo "Usage :"
    echo "$FUNCNAME folder"
    return
  fi
  local folder=$1

  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    find -E $folder -iregex '.*\.('$SRC_FILES_EXTENSIONS')'
  else
    #Rhahhh, lovely
    local extensions_escaped=$(echo $SRC_FILES_EXTENSIONS | sed s/\|/\\\\\|/g)
    #echo "extensions_escaped:$extensions_escaped"
    find $folder -iregex '.*\.\('$extensions_escaped'\)$'
  fi
}

function trim_trailing_space_all_source_files() {
  for f in $(find_source_files .); do trim_trailing_space $f;done
}

3

Для тих, хто шукає ефективність (багато файлів для обробки або величезні файли), використання +оператора повторення замість цього *робить команду більш ніж удвічі швидшою.

З GNU sed:

sed -Ei 's/[ \t]+$//' "$1"
sed -i 's/[ \t]\+$//' "$1"   # The same without extended regex

Я також швидко відзначив щось інше: використання [ \t]замість того, щоб [[:space:]]значно прискорити процес (GNU sed v4.4):

sed -Ei 's/[ \t]+$//' "$1"

real    0m0,335s
user    0m0,133s
sys 0m0,193s

sed -Ei 's/[[:space:]]+$//' "$1"

real    0m0,838s
user    0m0,630s
sys 0m0,207s

sed -Ei 's/[ \t]*$//' "$1"

real    0m0,882s
user    0m0,657s
sys 0m0,227s

sed -Ei 's/[[:space:]]*$//' "$1"

real    0m1,711s
user    0m1,423s
sys 0m0,283s

1

Задля розваги:

#!/bin/bash

FILE=$1

if [[ -z $FILE ]]; then
   echo "You must pass a filename -- exiting" >&2
   exit 1
fi

if [[ ! -f $FILE ]]; then
   echo "There is not file '$FILE' here -- exiting" >&2
   exit 1
fi

BEFORE=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

# >>>>>>>>>>
sed -i.bak -e's/[ \t]*$//' "$FILE"
# <<<<<<<<<<

AFTER=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

if [[ $? != 0 ]]; then
   echo "Some error occurred" >&2
else
   echo "Filtered '$FILE' from $BEFORE characters to $AFTER characters"
fi

0

У конкретному випадку sed, то -iваріант , що інші вже згадувалося далеко і далеко найпростіший і розсудлива один.

У більш загальному випадку spongeіз moreutilsколекції виконується саме те, що ви хочете: він дозволяє замінити файл результатом обробки, таким чином, спеціально розроблений для того, щоб не допустити, щоб крок обробки не натрапив на себе, перезаписавши той самий файл, який він працює на. Процитуйте сторінку spongeчоловіка:

губка зчитує стандартне введення та записує його у вказаний файл. На відміну від переадресації оболонки, губка вбирає весь її вхід до написання вихідного файлу. Це дозволяє побудувати трубопроводи, які читаються з та записуються в один і той же файл.

https://joeyh.name/code/moreutils/


-1

Щоб викреслити лише пробіли (у моєму випадку пробіли та вкладки) з рядків, що мають принаймні один символ, що не має пробілів (пусті рядки з відступом не торкаються):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.