【问题标题】:Running a script, taking its output and printing it from c-program运行脚本,获取其输出并从 c 程序打印
【发布时间】:2021-03-14 13:01:17
【问题描述】:

我正在开发一个程序,该程序从脚本获取输出,存储它并以某种方式打印输出,更具体地说是:(名字,中间名/姓氏,键)或(中间名/姓氏,第一名称,密钥)等...

所以我编写了程序,但是当我尝试运行它时,我不断收到分段失败。 编译很好,脚本可以工作,因为我测试过。

有什么可以做的不同或改进的建议吗?

代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>



struct memberinfo  {
char firstname[250]; // all names except last name
char surname [100]; // surname
int  pub_key;         // 0 if the user does not have a key
};

typedef struct memberinfo acc_t;
int counter = 0;

void les_data   (acc_t *classlist);
void skriv_data (acc_t *classlist, char * select);

int main(int argc, char *argv[]) {

     acc_t classlist[200];
     char *ch = NULL;

     if(argc == 2){//checking for additional argument on execute
     ch = argv[1];//pointer becomes main argument
     }
     else if(argc < 2){//if there is less then 2 arguments
     ch = getenv("V1");//pointer gets the default environmental variable.
     }
     else{//same as above, this just makes sure that "ch" don't return with NULL value
     argc == 1;
     ch = getenv("V1");
     }

     read_data  (classlist);//calling the method for executing the script and store output
     print_data(classlist, ch);//calling the method for printing the data

     return 0;
 }


 void read_data(acc_t *classlist) {

    FILE * list;
    char *name, *fname, *lname, *tmp;
    char usertmp[1000], buffer[BUFSIZ+1];//usertmp = temporary list, buffer contain the scripts output     
    int cnt, i, chars_read;

    memset(buffer, '\0', sizeof(buffer));//memsetting buffer with all 0

    list = popen("./classlist.sh", "r");//opening and running the script in a read mode

    while(list!=NULL){ //if list is not NULL, we begin to read from it

        chars_read = fread(buffer, sizeof(char), BUFSIZ, list);//reading from scripts output,
                                                                   //storing in buffer.

        while(chars_read >0){
             //while there are still characters in file we do:



                tmp = buffer;//setting pointer to the text stored in buffer
                for(i = 0; tmp[i]!= '\n'; i++){//for-loop running until a new line is detected        
                    if(tmp[i]== ' '){ //if there is a space in text the counter increases
                            cnt++;
                    }//if end
                }//for-loop end
            name = strtok(buffer,": ");//the complete name is taken from the text in buffer        
            strcpy(usertmp, name);//the name is copied to usertmp array

            for(i = 0; i <(cnt-2); i++){//for-loop

                name = strtok(NULL, ": ");
                strcat(strcat(usertmp, " "), name); //nested strcat??
            }//for-loop end                            // saves the whole name in usertmp[]       


            name = NULL;//setting name to null, clearing the memmory
            strcpy(classlist[counter].firstname, name);//copying first name in array     

            name = strtok(NULL, ": ");//setting to NULL to skip first name
            strcpy(classlist[counter].surname, usertmp);//copy middle/last name

            name = strtok(NULL, ": ");//setting to null to skipp the middle/last name      
            classlist[counter].pub_key = atoi(name);//copy the int key to array

            counter ++;

        }//if(chars_read) end
        pclose(list);
    }//while end


}//read_data end



void print_data (acc_t *classlist, char *select) {

   switch (select[0])
   {
      case 'k':
      printf("%s %s %d\n", classlist[counter].firstname, classlist[counter].surname, 
      classlist[counter].pub_key);
       //printf("Printing out class data:\n");
         break;

      default:
      break;
    }
 }

所以脚本会像这样打印一组成员:

脚本:

#! /bin/bash
grep da2001-2020 /etc/group |cut -f4 -d: > group.txt  
#Getting a list of members

tr -s ',' '\n' < group.txt |sort -k 1 -t',' -n > snm.txt 
#putting numbers in a order

sed 's/^.//' snm.txt > cnm.txt
#removing "s" in front of student number.

rm group.txt #delting group file


cut -f1,5 -d: /etc/passwd |cut -f1 -d, |sort -u > big_list.txt 
 #extracting names from file   
  
grep -f snm.txt big_list.txt   > final.txt  
#comparing the two lists

wc -l < final.txt > amount.txt
 #counting students

for id in $(cat cnm.txt); 
#scaning student numbers that were selected
do

File=$(curl -k --silent -q https://website.com/~$id/pub_key.asc| grep -o 
"BEGIN PGP PUBLIC KEY BLOCK")                                                                                                      
 #curling a request for file indicator pub_key.asc

 if [ "$File" ];
    then
            grep "$id" final.txt | cut -f2 -d: | sort > verification.txt 
             echo -e "$(cat verification.txt): 1 " #indicating has key
            #placing the students who have a key in a file with indicator
   else
          grep "$id" final.txt | cut -f2 -d: | sort > verification.txt 
            echo -e "$(cat verification.txt): 0 " #indicator no key
            #placing the students who dont have key in a file with indicator
     fi



  done 2>&1 | tee classlist.txt #exit loop, print and save output file


  rm amount.txt
  rm cnm.txt 
  rm big_list.txt
  rm snm.txt
  rm verification.txt
  rm final.txt

【问题讨论】:

  • 您对代码中的分段发生位置有任何猜测吗?
  • 你应该为你的转发函数使用原型,或者把它们放在main之前。另外:argc == 1;,你想在那里做什么?
  • 请启用/检查编译器警告。我收到 3 个警告,2 个错误。除了 David 提到的,您还需要使用原型声明函数 read_data()print_data()
  • ...以及read_data() 中针对cnt++; 未初始化变量 的另一个警告。
  • @ryyker 错误是针对没有原型的两个函数。首先每个都有一个警告,当函数本身被编译时,错误来自一个不匹配的定义(与编译器之前的假设相矛盾)。 MSVC:popen()pclose() 也出现错误,但这是意料之中的。

标签: c linux subprocess popen strtok


【解决方案1】:

我可以帮助你了解如何调试这种东西,

当你运行程序时,你会得到这样的分段错误,对吧?

Segmentation fault (core dumped)

这些调试起来很棘手,因为它没有给你太多的工作空间。 但是您应该在可执行文件所在的文件夹中留下一个名为 core 的文件。

核心文件对于跟踪分段错误非常有用,其他人已经解释了如何使用它比我更好:Core dump file analysis


如果您按照所有这些步骤操作,您应该能够找到导致崩溃的程序行:

#1  0x0000000000400ab9 in read_data (classlist=0x7ffd308e82a0) at test.c:82
82              strcpy(classlist[counter].firstname, name);//copying first name in array    

从那里,你知道这条线导致了崩溃。

从那里开始,strcpy 的使用存在问题。它导致了崩溃,因为name 设置为NULL,而strcpy 期望它的参数指向一个缓冲区,而NULL 也不是。

【讨论】:

  • 谢谢!即使使用“ls -a”我也找不到“核心文件”但是这种方法可以与 dgb 一起使用吗?另外,我很难理解,如何在不“重置”名称指针的情况下解析名称。但我会试一试并阅读更多内容。再次感谢。
  • 对于strcpy 用法,strcpy(dest, src) 将存储在src 中的字符串复制到dest 指向的内存中。如果 ``src` 是NULL,那么该函数将尝试取消引用一个非法的空指针。另外-我的回答假设您使用的是Linux。如果您使用的是 Windows 或 Mac,我认为答案会有所不同。如果不存在 core 文件,则需要进行特定于发行版的配置更改以启用核心转储。只是好奇,dgb 是什么?我找不到任何关于它的信息:)
  • 这是我的错,我提到了默认调试器 gdb。我得到了 strcpy 部分,但我的问题是我需要将名字和姓氏都保存在同一个数组中,而 strtok 不会破坏字符串。所以你的意思是我不需要耗尽内存,只需让“名称”从 strcpy 的任何位置开始?是的,我在 linux (debbian) 上。但是,如果我通过 bash 终端上的 vscode 远程连接到服务器,这有什么关系吗?我认为应该没问题:/
  • 哥奇亚。 1) 我认为ulimit -c unlimited 将为 Debian 启用核心转储,然后 2) 读取该链接,它有使用gdb 调试核心转储的说明。我认为您还没有使用strtokstrcpy 函数在正确的页面上。我不打算在这里回答,但我建议在每次这些变量更改之前和之后打印出tmpnameusertmp 变量和printf(),这样可能更好地说明这些函数是如何工作的。跨度>
  • 这是一个很棒的提示!我通常会这样做,但是我打印出来看看会发生什么 :) 谢谢,我 3 个月前才开始编码,所以我在这方面还是有点新的。有很多东西需要阅读,尝试并失败。
猜你喜欢
  • 2018-01-15
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-13
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多