كيف تقرأ ملف إلى متغير في الصدف؟

سئل على ١٥ سبتمبر ٢٠١١  ·  تمت مشاهدة 968.9k مرة  ·  مصدر

kaka picture
في ١٥ سبتمبر ٢٠١١

أريد قراءة ملف وحفظه في متغير ، لكني بحاجة إلى الاحتفاظ بالمتغير وليس فقط طباعة الملف. كيف يمكنني أن أفعل هذا؟ لقد كتبت هذا البرنامج النصي ولكنه ليس ما احتاجه تمامًا:

#!/bin/sh
while read LINE  
do  
  echo $LINE  
done <$1  
echo 11111-----------  
echo $LINE  

في البرنامج النصي الخاص بي ، يمكنني إعطاء اسم الملف كمعامل ، لذلك إذا كان الملف يحتوي على "aaaa" ، على سبيل المثال ، فسيتم طباعة هذا:

aaaa
11111-----

ولكن هذا فقط يطبع الملف على الشاشة ، وأريد حفظه في متغير! هل هناك طريقة سهلة للقيام بذلك؟

الإجابات

Alan Gutierrez picture
في ٢٧ مايو ٢٠١٢
1154

في النظام الأساسي المشترك ، القاسم المشترك الأدنى sh تستخدمه:

#!/bin/sh
value=`cat config.txt`
echo "$value"

في bash أو zsh ، لقراءة ملف كامل إلى متغير بدون استدعاء cat :

#!/bin/bash
value=$(<config.txt)
echo "$value"

إن استدعاء cat في bash أو zsh لإفساد ملف يعتبر استخدامًا غير مفيد لـ Cat .

لاحظ أنه ليس من الضروري اقتباس استبدال الأمر للحفاظ على الأسطر الجديدة.

انظر: ويكي Bash Hacker - استبدال الأوامر - التخصصات .

brain picture
في ١٥ سبتمبر ٢٠١١
88

إذا كنت تريد قراءة الملف بأكمله في متغير:

#!/bin/bash
value=`cat sources.xml`
echo $value

إذا كنت تريد قراءته سطراً سطراً:

while read line; do    
    echo $line    
done < file.txt

اثنين من المزالق الهامة

التي تجاهلتها الإجابات الأخرى حتى الآن:

  1. إزالة السطر الجديد من توسيع الأمر
  2. إزالة حرف NUL

إزالة السطر الجديد من توسيع الأمر

هذه مشكلة بالنسبة لـ:

value="$(cat config.txt)"

اكتب الحلول ، ولكن ليس للحلول المستندة إلى read .

يزيل توسيع الأمر الأسطر الجديدة اللاحقة:

S="$(printf "a\n")"
printf "$S" | od -tx1

المخرجات:

0000000 61
0000001

هذا يكسر الطريقة الساذجة للقراءة من الملفات:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"

حل POSIX: قم بإلحاق حرف إضافي بتوسيع الأمر وإزالته لاحقًا:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1

المخرجات:

0000000 61 0a 0a
0000003

حل POSIX تقريبًا: تشفير ASCII. انظر أدناه.

إزالة حرف NUL

لا توجد طريقة Bash عاقلة لتخزين أحرف NUL في المتغيرات .

يؤثر هذا على كل من التوسع وحلول read ، ولا أعرف أي حل جيد لذلك.

مثال:

printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1

المخرجات:

0000000 61 00 62
0000003

0000000 61 62
0000002

ها ، لقد ذهب NUL لدينا!

الحلول:

  • تشفير ASCII. انظر أدناه.

  • استخدم امتداد bash $"" Literals:

    S=$"a\0b"
    printf "$S" | od -tx1
    

    يعمل فقط مع القيم الحرفية ، لذا لا يفيد في القراءة من الملفات.

حل بديل للمخاطر

قم بتخزين إصدار uuencode base64 من الملف في المتغير ، وفك الشفرة قبل كل استخدام:

FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"

انتاج:

0000000 61 00 0a
0000003

uuencode و udecode هما POSIX 7 ولكن ليس في Ubuntu 12.04 افتراضيًا (الحزمة sharutils ) ... لا أرى بديل POSIX 7 لعملية bash <() امتداد الاستبدال باستثناء الكتابة إلى آخر ملف...

بالطبع ، هذا بطيء وغير مريح ، لذا أعتقد أن الإجابة الحقيقية هي: لا تستخدم Bash إذا كان ملف الإدخال يحتوي على أحرف NUL.

angelo.mastro picture
في ٣١ أغسطس ٢٠١٨
5

هذا يعمل بالنسبة لي: v=$(cat <file_path>) echo $v

dimo414 picture
في ٢٩ مارس ٢٠١٨
2

كما يلاحظ سيرو سانتيلي ، فإن استخدام استبدالات الأوامر سيؤدي إلى إسقاط الأسطر الجديدة اللاحقة. يعد حلهم البديل لإضافة أحرف زائدة أمرًا رائعًا ، ولكن بعد استخدامه لبعض الوقت قررت أنني بحاجة إلى حل لا يستخدم استبدال الأوامر على الإطلاق.

يستخدم طريقي الآن read مع علامة printf المبنية -v لقراءة محتويات stdin مباشرة في المتغير.

# Reads stdin into a variable, accounting for trailing newlines. Avoids
# needing a subshell or command substitution.
# Note that NUL bytes are still unsupported, as Bash variables don't allow NULs.
# See https://stackoverflow.com/a/22607352/113632
read_input() {
  # Use unusual variable names to avoid colliding with a variable name
  # the user might pass in (notably "contents")
  : "${1:?Must provide a variable to read into}"
  if [[ "$1" == '_line' || "$1" == '_contents' ]]; then
    echo "Cannot store contents to $1, use a different name." >&2
    return 1
  fi

  local _line _contents=()
   while IFS='' read -r _line; do
     _contents+=("$_line"$'\n')
   done
   # include $_line once more to capture any content after the last newline
   printf -v "$1" '%s' "${_contents[@]}" "$_line"
}

هذا يدعم المدخلات مع أو بدون أسطر جديدة لاحقة.

استخدام المثال:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file
L&#233;a Gris picture
في ١١ يوليو ٢٠٢٠
1

مع bash يمكنك استخدام read مثل tis:

#!/usr/bin/env bash

{ IFS= read -rd '' value <config.txt;} 2>/dev/null

printf '%s' "$value"

لاحظ أن:

  • يتم الاحتفاظ بالسطر الجديد الأخير.

  • يتم إسكات stderr إلى /dev/null عن طريق إعادة توجيه كتلة الأوامر بأكملها ، ولكن يتم الاحتفاظ بحالة إرجاع الأمر read ، إذا احتاج المرء للتعامل مع شروط خطأ القراءة.

Prakash D picture
في ١٧ ديسمبر ٢٠١٨
-2

يمكنك الوصول إلى سطر واحد في كل مرة عن طريق حلقة for

#!/bin/bash -eu

#This script prints contents of /etc/passwd line by line

FILENAME='/etc/passwd'
I=0
for LN in $(cat $FILENAME)
do
    echo "Line number $((I++)) -->  $LN"
done

انسخ المحتوى بالكامل إلى ملف (مثل line.sh) ؛ نفذ - اعدم

chmod +x line.sh
./line.sh